diff options
| author | Darius Jahandarie <djahandarie@gmail.com> | 2023-11-08 03:11:35 +0900 | 
|---|---|---|
| committer | Darius Jahandarie <djahandarie@gmail.com> | 2023-11-08 03:23:17 +0900 | 
| commit | 0f4d36938fd0d844f548aa5a7f7e7842df8dfb41 (patch) | |
| tree | 5b6be3620a557d0b9177047003f6d742d9d2a32d | |
| parent | ef79eab44bfd000792c610b968b5ceefd41e76a0 (diff) | |
Switch to vitest for ESM support; other fixes
70 files changed, 13809 insertions, 10596 deletions
diff --git a/.eslintrc.json b/.eslintrc.json index 99c2383a..dce9b344 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,7 @@          "plugin:jsonc/recommended-with-json"      ],      "parserOptions": { -        "ecmaVersion": 11, +        "ecmaVersion": 2022,          "sourceType": "module",          "ecmaFeatures": {              "globalReturn": false, @@ -14,7 +14,7 @@      },      "env": {          "browser": true, -        "es2018": true, +        "es2022": true,          "webextensions": true      },      "plugins": [ @@ -24,7 +24,8 @@          "jsonc"      ],      "ignorePatterns": [ -        "/ext/lib/" +        "/ext/lib/", +        "/dev/lib/handlebars/"      ],      "rules": {          "arrow-parens": [ @@ -181,6 +182,7 @@                  "after": true              }          ], +        "no-implicit-globals": "error",          "no-trailing-spaces": "error",          "no-whitespace-before-property": "error",          "object-curly-spacing": [ @@ -379,31 +381,6 @@          },          {              "files": [ -                "ext/**/*.js" -            ], -            "excludedFiles": [ -                "ext/js/core.js", -                "ext/js/accessibility/google-docs.js", -                "ext/js/**/sandbox/**/*.js" -            ], -            "globals": {} -        }, -        { -            "files": [ -                "ext/**/*.js" -            ], -            "excludedFiles": [ -                "ext/js/core.js", -                "ext/js/accessibility/google-docs.js", -                "ext/js/yomichan.js", -                "ext/js/**/sandbox/**/*.js" -            ], -            "globals": { -                "yomichan": "readonly" -            } -        }, -        { -            "files": [                  "ext/js/yomichan.js"              ],              "globals": { @@ -415,57 +392,26 @@                  "test/**/*.js",                  "dev/**/*.js"              ], -            "excludedFiles": [ -                "test/data/html/*.js" -            ], -            "parserOptions": { -                "ecmaVersion": 8, -                "sourceType": "module" -            },              "env": {                  "browser": false, -                "es2017": true,                  "node": true,                  "webextensions": false              }          },          {              "files": [ -                "ext/js/language/dictionary-worker-main.js" +                "test/data/html/*.js"              ],              "parserOptions": { -                "sourceType": "module" -            } -        }, -        { -            "files": [ -                "playwright.config.js" -            ], -            "env": { -                "browser": false, -                "es2017": true, -                "node": true, -                "webextensions": false +                "sourceType": "script"              }, -            "rules": { -                "no-undefined": "off" -            } -        }, -        { -            "files": [ -                "integration.spec.js", -                "playwright-util.js", -                "visual.spec.js" -            ],              "env": { -                "browser": false, -                "es2017": true, -                "node": true, +                "browser": true, +                "node": false,                  "webextensions": false              },              "rules": { -                "no-undefined": "off", -                "no-empty-pattern": "off" +                "no-implicit-globals": "off"              }          },          { @@ -506,13 +452,13 @@              "env": {                  "browser": false,                  "serviceworker": true, -                "es2017": true,                  "webextensions": true              },              "globals": {                  "FileReader": "readonly",                  "Intl": "readonly", -                "crypto": "readonly" +                "crypto": "readonly", +                "AbortController": "readonly"              }          },          { @@ -530,29 +476,52 @@              "env": {                  "browser": false,                  "worker": true, -                "es2017": true,                  "webextensions": true              }          },          {              "files": [ -                "ext/js/**/*.js" -            ], -            "excludedFiles": [ -                "ext/js/core.js", -                "ext/js/**/*main.js" +                "playwright.config.js"              ], +            "env": { +                "browser": false, +                "node": true, +                "webextensions": false +            },              "rules": { -                "no-implicit-globals": "error" +                "no-undefined": "off"              }          },          {              "files": [ -                "ext/js/**/*.js" +                "integration.spec.js", +                "playwright-util.js", +                "visual.spec.js"              ], -            "globals": { -                "AbortController": "readonly" +            "env": { +                "browser": false, +                "node": true, +                "webextensions": false +            }, +            "rules": { +                "no-undefined": "off", +                "no-empty-pattern": "off"              } +        }, +        { +            "files": [ +                "test/**" +            ], +            "plugins": [ +                "vitest" +            ], +            "extends": [ +                "plugin:vitest/recommended" +            ], +            "rules": { +                "vitest/prefer-to-be": "off" +            }, +            "env": {}          }      ]  } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc70925f..358ac1de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,8 @@ jobs:        - name: Install dependencies          run: npm ci -      - name: Lint -        run: npm run test-lint +      - name: Lint JS +        run: npm run test-lint-js          env:            CI: true @@ -36,6 +36,9 @@ jobs:          env:            CI: true +      - name: Build Libs +        run: npm run build-libs +        - name: Tests          run: npm run test-code          env: @@ -1,10 +1,17 @@ +.DS_Store +  node_modules/ +  builds/ -.DS_Store +  dictionaries/ +  /test-results/  /playwright-report/  /playwright/.cache/  /test/playwright/__screenshots__/ +  ext/manifest.json -ext/lib/ + +ext/lib/* +!ext/lib/__mocks__/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b19e68e..3b48236f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ There are two scripts to build the extension to a packaged file for various buil  - [build.bat](build.bat) on Windows  - [build.sh](build.sh) on Linux -Both of these files are convenience scripts which invoke <code>node [dev/build.js](dev/build.js)</code>. +Both of these files are convenience scripts which invoke <code>node [dev/bin/build.js](dev/bin/build.js)</code>.  The build script can produce several different build files based on manifest configurations defined in  [manifest-variants.json](dev/data/manifest-variants.json).  Several command line arguments are available for these scripts: diff --git a/dev/bin/build-libs.js b/dev/bin/build-libs.js new file mode 100644 index 00000000..07d27188 --- /dev/null +++ b/dev/bin/build-libs.js @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {buildLibs} from '../build-libs.js'; + +buildLibs(); diff --git a/dev/build.js b/dev/bin/build.js index 1e6ef1d0..282f0414 100644 --- a/dev/build.js +++ b/dev/bin/build.js @@ -16,17 +16,17 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const readline = require('readline'); -const childProcess = require('child_process'); -const util = require('./util'); -const {getAllFiles, getArgs, testMain} = util; -const {ManifestUtil} = require('./manifest-util'); -const Ajv = require('ajv'); -const standaloneCode = require('ajv/dist/standalone').default; -const buildLibs = require('./build-libs.js').buildLibs; +import assert from 'assert'; +import childProcess from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import readline from 'readline'; +import {fileURLToPath} from 'url'; +import {buildLibs} from '../build-libs.js'; +import {ManifestUtil} from '../manifest-util.js'; +import {getAllFiles, getArgs, testMain} from '../util.js'; + +const dirname = path.dirname(fileURLToPath(import.meta.url));  async function createZip(directory, excludeFiles, outputFileName, sevenZipExes, onUpdate, dryRun) {      try { @@ -61,7 +61,7 @@ async function createZip(directory, excludeFiles, outputFileName, sevenZipExes,  }  async function createJSZip(directory, excludeFiles, outputFileName, onUpdate, dryRun) { -    const JSZip = util.JSZip; +    const JSZip = null;      const files = getAllFiles(directory);      removeItemsFromArray(files, excludeFiles);      const zip = new JSZip(); @@ -132,19 +132,6 @@ async function build(buildDir, extDir, manifestUtil, variantNames, manifestPath,          process.stdout.write(message);      }; -    process.stdout.write('Building schema validators using ajv\n'); -    const schemaDir = path.join(extDir, 'data/schemas/'); -    const schemaFileNames = fs.readdirSync(schemaDir); -    const schemas = schemaFileNames.map((schemaFileName) => JSON.parse(fs.readFileSync(path.join(schemaDir, schemaFileName)))); -    const ajv = new Ajv({schemas: schemas, code: {source: true, esm: true}}); -    const moduleCode = standaloneCode(ajv); - -    // https://github.com/ajv-validator/ajv/issues/2209 -    const patchedModuleCode = moduleCode.replaceAll('require("ajv/dist/runtime/ucs2length").default', 'import("/lib/ucs2length.js").default'); - -    fs.writeFileSync(path.join(extDir, 'lib/validate-schemas.js'), patchedModuleCode); - -      process.stdout.write(`Version: ${yomitanVersion}...\n`);      for (const variantName of variantNames) { @@ -193,7 +180,7 @@ function ensureFilesExist(directory, files) {  } -async function main(argv) { +export async function main(argv) {      const args = getArgs(argv, new Map([          ['all', false],          ['default', false], @@ -210,7 +197,7 @@ async function main(argv) {      const manifestUtil = new ManifestUtil(); -    const rootDir = path.join(__dirname, '..'); +    const rootDir = path.join(dirname, '..', '..');      const extDir = path.join(rootDir, 'ext');      const buildDir = path.join(rootDir, 'builds');      const manifestPath = path.join(extDir, 'manifest.json'); @@ -234,12 +221,4 @@ async function main(argv) {      }  } - -if (require.main === module) { -    testMain(main, process.argv.slice(2)); -} - - -module.exports = { -    main -}; +testMain(main, process.argv.slice(2)); diff --git a/test/test-build-libs.js b/dev/bin/dictionary-validate.js index 496f43f8..78ad5198 100644 --- a/test/test-build-libs.js +++ b/dev/bin/dictionary-validate.js @@ -1,6 +1,6 @@  /*   * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2022  Yomichan Authors + * Copyright (C) 2020-2022  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 @@ -16,27 +16,25 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const assert = require('assert'); -const {getBuildTargets} = require('../dev/build-libs'); +import {testDictionaryFiles} from '../dictionary-validate.js';  async function main() { -    try { -        for (const {path: path2, build} of getBuildTargets()) { -            let expectedContent = await build(); -            if (typeof expectedContent !== 'string') { -                // Buffer -                expectedContent = expectedContent.toString('utf8'); -            } -            const actualContent = fs.readFileSync(path2, {encoding: 'utf8'}); -            assert.strictEqual(actualContent, expectedContent); -        } -    } catch (e) { -        console.error(e); -        process.exit(-1); +    const dictionaryFileNames = process.argv.slice(2); +    if (dictionaryFileNames.length === 0) { +        console.log([ +            'Usage:', +            '  node dictionary-validate [--ajv] <dictionary-file-names>...' +        ].join('\n'));          return;      } -    process.exit(0); + +    let mode = null; +    if (dictionaryFileNames[0] === '--ajv') { +        mode = 'ajv'; +        dictionaryFileNames.splice(0, 1); +    } + +    await testDictionaryFiles(mode, dictionaryFileNames);  } -if (require.main === module) { main(); } +main(); diff --git a/test/test-css-json.js b/dev/bin/generate-css-json.js index ddeee6bd..48b42c65 100644 --- a/test/test-css-json.js +++ b/dev/bin/generate-css-json.js @@ -1,6 +1,6 @@  /*   * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2021-2022  Yomichan Authors + * Copyright (C) 2020-2022  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 @@ -16,22 +16,14 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {formatRulesJson, generateRules} = require('../dev/css-to-json-util'); -const {getTargets} = require('../dev/generate-css-json'); - +import fs from 'fs'; +import {formatRulesJson, generateRules, getTargets} from '../generate-css-json.js';  function main() {      for (const {cssFile, overridesCssFile, outputPath} of getTargets()) { -        const actual = fs.readFileSync(outputPath, {encoding: 'utf8'}); -        const expected = formatRulesJson(generateRules(cssFile, overridesCssFile)); -        assert.deepStrictEqual(actual, expected); +        const json = formatRulesJson(generateRules(cssFile, overridesCssFile)); +        fs.writeFileSync(outputPath, json, {encoding: 'utf8'});      }  } - -if (require.main === module) { -    testMain(main, process.argv.slice(2)); -} +main(); diff --git a/dev/bin/schema-validate.js b/dev/bin/schema-validate.js new file mode 100644 index 00000000..86cfebae --- /dev/null +++ b/dev/bin/schema-validate.js @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import fs from 'fs'; +import performance from 'perf_hooks'; +import {createJsonSchema} from '../util.js'; + +function main() { +    const args = process.argv.slice(2); +    if (args.length < 2) { +        console.log([ +            'Usage:', +            '  node schema-validate [--ajv] <schema-file-name> <data-file-names>...' +        ].join('\n')); +        return; +    } + +    let mode = null; +    if (args[0] === '--ajv') { +        mode = 'ajv'; +        args.splice(0, 1); +    } + +    const schemaSource = fs.readFileSync(args[0], {encoding: 'utf8'}); +    const schema = JSON.parse(schemaSource); + +    for (const dataFileName of args.slice(1)) { +        const start = performance.now(); +        try { +            console.log(`Validating ${dataFileName}...`); +            const dataSource = fs.readFileSync(dataFileName, {encoding: 'utf8'}); +            const data = JSON.parse(dataSource); +            createJsonSchema(mode, schema).validate(data); +            const end = performance.now(); +            console.log(`No issues detected (${((end - start) / 1000).toFixed(2)}s)`); +        } catch (e) { +            const end = performance.now(); +            console.log(`Encountered an error (${((end - start) / 1000).toFixed(2)}s)`); +            console.warn(e); +        } +    } +} + + +main(); diff --git a/dev/build-libs.js b/dev/build-libs.js index 497206c9..8320a947 100644 --- a/dev/build-libs.js +++ b/dev/build-libs.js @@ -16,9 +16,15 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const esbuild = require('esbuild'); +import Ajv from 'ajv'; +import standaloneCode from 'ajv/dist/standalone/index.js'; +import esbuild from 'esbuild'; +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const extDir = path.join(dirname, '..', 'ext');  async function buildLib(p) {      await esbuild.build({ @@ -28,13 +34,13 @@ async function buildLib(p) {          sourcemap: true,          target: 'es2020',          format: 'esm', -        outfile: path.join(__dirname, '..', 'ext', 'lib', path.basename(p)), +        outfile: path.join(extDir, 'lib', path.basename(p)),          external: ['fs']      });  } -async function buildLibs() { -    const devLibPath = path.join(__dirname, 'lib'); +export async function buildLibs() { +    const devLibPath = path.join(dirname, 'lib');      const files = await fs.promises.readdir(devLibPath, {          withFileTypes: true      }); @@ -43,10 +49,15 @@ async function buildLibs() {              await buildLib(path.join(devLibPath, f.name));          }      } -} -if (require.main === module) { buildLibs(); } +    const schemaDir = path.join(extDir, 'data/schemas/'); +    const schemaFileNames = fs.readdirSync(schemaDir); +    const schemas = schemaFileNames.map((schemaFileName) => JSON.parse(fs.readFileSync(path.join(schemaDir, schemaFileName)))); +    const ajv = new Ajv({schemas: schemas, code: {source: true, esm: true}}); +    const moduleCode = standaloneCode(ajv); -module.exports = { -    buildLibs -}; +    // https://github.com/ajv-validator/ajv/issues/2209 +    const patchedModuleCode = "import {ucs2length} from './ucs2length.js';" + moduleCode.replaceAll('require("ajv/dist/runtime/ucs2length").default', 'ucs2length'); + +    fs.writeFileSync(path.join(extDir, 'lib/validate-schemas.js'), patchedModuleCode); +} diff --git a/dev/css-to-json-util.js b/dev/css-to-json-util.js deleted file mode 100644 index 79aae3c9..00000000 --- a/dev/css-to-json-util.js +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2021-2022  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/>. - */ - -const fs = require('fs'); -const css = require('css'); - -function indexOfRule(rules, selectors) { -    const jj = selectors.length; -    for (let i = 0, ii = rules.length; i < ii; ++i) { -        const ruleSelectors = rules[i].selectors; -        if (ruleSelectors.length !== jj) { continue; } -        let okay = true; -        for (let j = 0; j < jj; ++j) { -            if (selectors[j] !== ruleSelectors[j]) { -                okay = false; -                break; -            } -        } -        if (okay) { return i; } -    } -    return -1; -} - -function removeProperty(styles, property, removedProperties) { -    let removeCount = removedProperties.get(property); -    if (typeof removeCount !== 'undefined') { return removeCount; } -    removeCount = 0; -    for (let i = 0, ii = styles.length; i < ii; ++i) { -        const key = styles[i][0]; -        if (key !== property) { continue; } -        styles.splice(i, 1); -        --i; -        --ii; -        ++removeCount; -    } -    removedProperties.set(property, removeCount); -    return removeCount; -} - -function formatRulesJson(rules) { -    // Manually format JSON, for improved compactness -    // return JSON.stringify(rules, null, 4); -    const indent1 = '    '; -    const indent2 = indent1.repeat(2); -    const indent3 = indent1.repeat(3); -    let result = ''; -    result += '['; -    let index1 = 0; -    for (const {selectors, styles} of rules) { -        if (index1 > 0) { result += ','; } -        result += `\n${indent1}{\n${indent2}"selectors": `; -        if (selectors.length === 1) { -            result += `[${JSON.stringify(selectors[0], null, 4)}]`; -        } else { -            result += JSON.stringify(selectors, null, 4).replace(/\n/g, '\n' + indent2); -        } -        result += `,\n${indent2}"styles": [`; -        let index2 = 0; -        for (const [key, value] of styles) { -            if (index2 > 0) { result += ','; } -            result += `\n${indent3}[${JSON.stringify(key)}, ${JSON.stringify(value)}]`; -            ++index2; -        } -        if (index2 > 0) { result += `\n${indent2}`; } -        result += `]\n${indent1}}`; -        ++index1; -    } -    if (index1 > 0) { result += '\n'; } -    result += ']'; -    return result; -} - -function generateRules(cssFile, overridesCssFile) { -    const content1 = fs.readFileSync(cssFile, {encoding: 'utf8'}); -    const content2 = fs.readFileSync(overridesCssFile, {encoding: 'utf8'}); -    const stylesheet1 = css.parse(content1, {}).stylesheet; -    const stylesheet2 = css.parse(content2, {}).stylesheet; - -    const removePropertyPattern = /^remove-property\s+([\w\W]+)$/; -    const removeRulePattern = /^remove-rule$/; -    const propertySeparator = /\s+/; - -    const rules = []; - -    // Default stylesheet -    for (const rule of stylesheet1.rules) { -        if (rule.type !== 'rule') { continue; } -        const {selectors, declarations} = rule; -        const styles = []; -        for (const declaration of declarations) { -            if (declaration.type !== 'declaration') { console.log(declaration); continue; } -            const {property, value} = declaration; -            styles.push([property, value]); -        } -        if (styles.length > 0) { -            rules.push({selectors, styles}); -        } -    } - -    // Overrides -    for (const rule of stylesheet2.rules) { -        if (rule.type !== 'rule') { continue; } -        const {selectors, declarations} = rule; -        const removedProperties = new Map(); -        for (const declaration of declarations) { -            switch (declaration.type) { -                case 'declaration': -                    { -                        const index = indexOfRule(rules, selectors); -                        let entry; -                        if (index >= 0) { -                            entry = rules[index]; -                        } else { -                            entry = {selectors, styles: []}; -                            rules.push(entry); -                        } -                        const {property, value} = declaration; -                        removeProperty(entry.styles, property, removedProperties); -                        entry.styles.push([property, value]); -                    } -                    break; -                case 'comment': -                    { -                        const index = indexOfRule(rules, selectors); -                        if (index < 0) { throw new Error('Could not find rule with matching selectors'); } -                        const comment = declaration.comment.trim(); -                        let m; -                        if ((m = removePropertyPattern.exec(comment)) !== null) { -                            for (const property of m[1].split(propertySeparator)) { -                                const removeCount = removeProperty(rules[index].styles, property, removedProperties); -                                if (removeCount === 0) { throw new Error(`Property removal is unnecessary; ${property} does not exist`); } -                            } -                        } else if (removeRulePattern.test(comment)) { -                            rules.splice(index, 1); -                        } -                    } -                    break; -            } -        } -    } - -    // Remove empty -    for (let i = 0, ii = rules.length; i < ii; ++i) { -        if (rules[i].styles.length > 0) { continue; } -        rules.splice(i, 1); -        --i; -        --ii; -    } - -    return rules; -} - - -module.exports = { -    formatRulesJson, -    generateRules -}; diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index d44251e1..e6113b75 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -122,8 +122,7 @@              "inherit": "base",              "fileName": "yomitan-chrome.zip",              "excludeFiles": [ -                "background.html", -                "js/dom/native-simple-dom-parser.js" +                "background.html"              ]          },          { @@ -187,6 +186,13 @@                      ]                  },                  { +                    "action": "delete", +                    "path": [ +                        "background", +                        "type" +                    ] +                }, +                {                      "action": "set",                      "path": [                          "background", @@ -251,9 +257,7 @@                  "sw.js",                  "offscreen.html",                  "js/background/offscreen.js", -                "js/background/offscreen-main.js", -                "js/dom/simple-dom-parser.js", -                "lib/parse5.js" +                "js/background/offscreen-main.js"              ]          },          { @@ -302,9 +306,7 @@                  "sw.js",                  "offscreen.html",                  "js/background/offscreen.js", -                "js/background/offscreen-main.js", -                "js/dom/simple-dom-parser.js", -                "lib/parse5.js" +                "js/background/offscreen-main.js"              ]          },          { @@ -351,9 +353,7 @@                  "sw.js",                  "offscreen.html",                  "js/background/offscreen.js", -                "js/background/offscreen-main.js", -                "js/dom/simple-dom-parser.js", -                "lib/parse5.js" +                "js/background/offscreen-main.js"              ]          }      ] diff --git a/dev/database-vm.js b/dev/database-vm.js deleted file mode 100644 index d5570691..00000000 --- a/dev/database-vm.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const url = require('url'); -const path = require('path'); -const {JSZip} = require('./util'); -const {VM} = require('./vm'); -require('fake-indexeddb/auto'); - -const chrome = { -    runtime: { -        getURL: (path2) => { -            return url.pathToFileURL(path.join(__dirname, '..', 'ext', path2.replace(/^\//, ''))).href; -        } -    } -}; - -async function fetch(url2) { -    const extDir = path.join(__dirname, '..', 'ext'); -    let filePath; -    try { -        filePath = url.fileURLToPath(url2); -    } catch (e) { -        filePath = path.resolve(extDir, url2.replace(/^[/\\]/, '')); -    } -    await Promise.resolve(); -    const content = fs.readFileSync(filePath, {encoding: null}); -    return { -        ok: true, -        status: 200, -        statusText: 'OK', -        text: async () => Promise.resolve(content.toString('utf8')), -        json: async () => Promise.resolve(JSON.parse(content.toString('utf8'))) -    }; -} - -function atob(data) { -    return Buffer.from(data, 'base64').toString('ascii'); -} - -class DatabaseVM extends VM { -    constructor(globals={}) { -        super(Object.assign({ -            chrome, -            fetch, -            indexedDB: global.indexedDB, -            IDBKeyRange: global.IDBKeyRange, -            JSZip, -            atob -        }, globals)); -        this.context.window = this.context; -        this.indexedDB = global.indexedDB; -    } -} - -class DatabaseVMDictionaryImporterMediaLoader { -    async getImageDetails(content) { -        // Placeholder values -        return {content, width: 100, height: 100}; -    } -} - -module.exports = { -    DatabaseVM, -    DatabaseVMDictionaryImporterMediaLoader -}; diff --git a/dev/dictionary-validate.js b/dev/dictionary-validate.js index 0c926acc..eb40beda 100644 --- a/dev/dictionary-validate.js +++ b/dev/dictionary-validate.js @@ -16,12 +16,11 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const {performance} = require('perf_hooks'); -const {JSZip} = require('./util'); -const {createJsonSchema} = require('./schema-validate'); - +import fs from 'fs'; +import JSZip from 'jszip'; +import path from 'path'; +import {performance} from 'perf_hooks'; +import {createJsonSchema} from './schema-validate.js';  function readSchema(relativeFileName) {      const fileName = path.join(__dirname, relativeFileName); @@ -29,7 +28,6 @@ function readSchema(relativeFileName) {      return JSON.parse(source);  } -  async function validateDictionaryBanks(mode, zip, fileNameFormat, schema) {      let jsonSchema;      try { @@ -57,7 +55,7 @@ async function validateDictionaryBanks(mode, zip, fileNameFormat, schema) {      }  } -async function validateDictionary(mode, archive, schemas) { +export async function validateDictionary(mode, archive, schemas) {      const fileName = 'index.json';      const indexFile = archive.files[fileName];      if (!indexFile) { @@ -82,7 +80,7 @@ async function validateDictionary(mode, archive, schemas) {      await validateDictionaryBanks(mode, archive, 'tag_bank_?.json', schemas.tagBankV3);  } -function getSchemas() { +export function getSchemas() {      return {          index: readSchema('../ext/data/schemas/dictionary-index-schema.json'),          kanjiBankV1: readSchema('../ext/data/schemas/dictionary-kanji-bank-v1-schema.json'), @@ -95,8 +93,7 @@ function getSchemas() {      };  } - -async function testDictionaryFiles(mode, dictionaryFileNames) { +export async function testDictionaryFiles(mode, dictionaryFileNames) {      const schemas = getSchemas();      for (const dictionaryFileName of dictionaryFileNames) { @@ -115,33 +112,3 @@ async function testDictionaryFiles(mode, dictionaryFileNames) {          }      }  } - - -async function main() { -    const dictionaryFileNames = process.argv.slice(2); -    if (dictionaryFileNames.length === 0) { -        console.log([ -            'Usage:', -            '  node dictionary-validate [--ajv] <dictionary-file-names>...' -        ].join('\n')); -        return; -    } - -    let mode = null; -    if (dictionaryFileNames[0] === '--ajv') { -        mode = 'ajv'; -        dictionaryFileNames.splice(0, 1); -    } - -    await testDictionaryFiles(mode, dictionaryFileNames); -} - - -if (require.main === module) { main(); } - - -module.exports = { -    getSchemas, -    validateDictionary, -    testDictionaryFiles -}; diff --git a/dev/generate-css-json.js b/dev/generate-css-json.js index 787173ab..914c1452 100644 --- a/dev/generate-css-json.js +++ b/dev/generate-css-json.js @@ -16,12 +16,10 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const {testMain} = require('./util'); -const {formatRulesJson, generateRules} = require('./css-to-json-util'); +import fs from 'fs'; +import path from 'path'; -function getTargets() { +export function getTargets() {      return [          {              cssFile: path.join(__dirname, '..', 'ext/css/structured-content.css'), @@ -36,19 +34,150 @@ function getTargets() {      ];  } -function main() { -    for (const {cssFile, overridesCssFile, outputPath} of getTargets()) { -        const json = formatRulesJson(generateRules(cssFile, overridesCssFile)); -        fs.writeFileSync(outputPath, json, {encoding: 'utf8'}); +import css from 'css'; + +function indexOfRule(rules, selectors) { +    const jj = selectors.length; +    for (let i = 0, ii = rules.length; i < ii; ++i) { +        const ruleSelectors = rules[i].selectors; +        if (ruleSelectors.length !== jj) { continue; } +        let okay = true; +        for (let j = 0; j < jj; ++j) { +            if (selectors[j] !== ruleSelectors[j]) { +                okay = false; +                break; +            } +        } +        if (okay) { return i; }      } +    return -1;  } +function removeProperty(styles, property, removedProperties) { +    let removeCount = removedProperties.get(property); +    if (typeof removeCount !== 'undefined') { return removeCount; } +    removeCount = 0; +    for (let i = 0, ii = styles.length; i < ii; ++i) { +        const key = styles[i][0]; +        if (key !== property) { continue; } +        styles.splice(i, 1); +        --i; +        --ii; +        ++removeCount; +    } +    removedProperties.set(property, removeCount); +    return removeCount; +} -if (require.main === module) { -    testMain(main, process.argv.slice(2)); +export function formatRulesJson(rules) { +    // Manually format JSON, for improved compactness +    // return JSON.stringify(rules, null, 4); +    const indent1 = '    '; +    const indent2 = indent1.repeat(2); +    const indent3 = indent1.repeat(3); +    let result = ''; +    result += '['; +    let index1 = 0; +    for (const {selectors, styles} of rules) { +        if (index1 > 0) { result += ','; } +        result += `\n${indent1}{\n${indent2}"selectors": `; +        if (selectors.length === 1) { +            result += `[${JSON.stringify(selectors[0], null, 4)}]`; +        } else { +            result += JSON.stringify(selectors, null, 4).replace(/\n/g, '\n' + indent2); +        } +        result += `,\n${indent2}"styles": [`; +        let index2 = 0; +        for (const [key, value] of styles) { +            if (index2 > 0) { result += ','; } +            result += `\n${indent3}[${JSON.stringify(key)}, ${JSON.stringify(value)}]`; +            ++index2; +        } +        if (index2 > 0) { result += `\n${indent2}`; } +        result += `]\n${indent1}}`; +        ++index1; +    } +    if (index1 > 0) { result += '\n'; } +    result += ']'; +    return result;  } +export function generateRules(cssFile, overridesCssFile) { +    const content1 = fs.readFileSync(cssFile, {encoding: 'utf8'}); +    const content2 = fs.readFileSync(overridesCssFile, {encoding: 'utf8'}); +    const stylesheet1 = css.parse(content1, {}).stylesheet; +    const stylesheet2 = css.parse(content2, {}).stylesheet; + +    const removePropertyPattern = /^remove-property\s+([\w\W]+)$/; +    const removeRulePattern = /^remove-rule$/; +    const propertySeparator = /\s+/; -module.exports = { -    getTargets -}; +    const rules = []; + +    // Default stylesheet +    for (const rule of stylesheet1.rules) { +        if (rule.type !== 'rule') { continue; } +        const {selectors, declarations} = rule; +        const styles = []; +        for (const declaration of declarations) { +            if (declaration.type !== 'declaration') { console.log(declaration); continue; } +            const {property, value} = declaration; +            styles.push([property, value]); +        } +        if (styles.length > 0) { +            rules.push({selectors, styles}); +        } +    } + +    // Overrides +    for (const rule of stylesheet2.rules) { +        if (rule.type !== 'rule') { continue; } +        const {selectors, declarations} = rule; +        const removedProperties = new Map(); +        for (const declaration of declarations) { +            switch (declaration.type) { +                case 'declaration': +                    { +                        const index = indexOfRule(rules, selectors); +                        let entry; +                        if (index >= 0) { +                            entry = rules[index]; +                        } else { +                            entry = {selectors, styles: []}; +                            rules.push(entry); +                        } +                        const {property, value} = declaration; +                        removeProperty(entry.styles, property, removedProperties); +                        entry.styles.push([property, value]); +                    } +                    break; +                case 'comment': +                    { +                        const index = indexOfRule(rules, selectors); +                        if (index < 0) { throw new Error('Could not find rule with matching selectors'); } +                        const comment = declaration.comment.trim(); +                        let m; +                        if ((m = removePropertyPattern.exec(comment)) !== null) { +                            for (const property of m[1].split(propertySeparator)) { +                                const removeCount = removeProperty(rules[index].styles, property, removedProperties); +                                if (removeCount === 0) { throw new Error(`Property removal is unnecessary; ${property} does not exist`); } +                            } +                        } else if (removeRulePattern.test(comment)) { +                            rules.splice(index, 1); +                        } +                    } +                    break; +            } +        } +    } + +    // Remove empty +    for (let i = 0, ii = rules.length; i < ii; ++i) { +        if (rules[i].styles.length > 0) { continue; } +        rules.splice(i, 1); +        --i; +        --ii; +    } + +    return rules; +} diff --git a/dev/lib/ucs2length.js b/dev/lib/ucs2length.js index 2e4a01cd..3b370493 100644 --- a/dev/lib/ucs2length.js +++ b/dev/lib/ucs2length.js @@ -14,5 +14,7 @@   * You should have received a copy of the GNU General Public License   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -export {ucs2length} from 'ajv/dist/runtime/ucs2length'; +import ucs2length from 'ajv/dist/runtime/ucs2length.js'; +const ucs2length2 = ucs2length.default; +export {ucs2length2 as ucs2length}; diff --git a/dev/lib/z-worker.js b/dev/lib/z-worker.js new file mode 100644 index 00000000..f6a95ed3 --- /dev/null +++ b/dev/lib/z-worker.js @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2023  Yomitan 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/>. + */ +import '../../node_modules/@zip.js/zip.js/lib/z-worker.js'; diff --git a/dev/lib/zip.js b/dev/lib/zip.js index 7560f5f8..b6e85451 100644 --- a/dev/lib/zip.js +++ b/dev/lib/zip.js @@ -14,4 +14,4 @@   * You should have received a copy of the GNU General Public License   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -export * from '@zip.js/zip.js/lib/zip-full.js'; +export * from '@zip.js/zip.js/lib/zip.js'; diff --git a/dev/lint/global-declarations.js b/dev/lint/global-declarations.js deleted file mode 100644 index 7f90d227..00000000 --- a/dev/lint/global-declarations.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {getAllFiles} = require('../util'); - - -function escapeRegExp(string) { -    return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); -} - -function countOccurences(string, pattern) { -    return (string.match(pattern) || []).length; -} - -function getNewline(string) { -    const count1 = countOccurences(string, /(?:^|[^\r])\n/g); -    const count2 = countOccurences(string, /\r\n/g); -    const count3 = countOccurences(string, /\r(?:[^\n]|$)/g); -    if (count2 > count1) { -        return (count3 > count2) ? '\r' : '\r\n'; -    } else { -        return (count3 > count1) ? '\r' : '\n'; -    } -} - -function getSubstringCount(string, substring) { -    let count = 0; -    const pattern = new RegExp(`\\b${escapeRegExp(substring)}\\b`, 'g'); -    while (true) { -        const match = pattern.exec(string); -        if (match === null) { break; } -        ++count; -    } -    return count; -} - - -function validateGlobals(fileName, fix) { -    const pattern = /\/\*\s*global\s+([\w\W]*?)\*\//g; -    const trimPattern = /^[\s,*]+|[\s,*]+$/g; -    const splitPattern = /[\s,*]+/; -    const source = fs.readFileSync(fileName, {encoding: 'utf8'}); -    let match; -    let first = true; -    let endIndex = 0; -    let newSource = ''; -    const allGlobals = []; -    const newline = getNewline(source); -    while ((match = pattern.exec(source)) !== null) { -        if (!first) { -            console.error(`Encountered more than one global declaration in ${fileName}`); -            return false; -        } -        first = false; - -        const parts = match[1].replace(trimPattern, '').split(splitPattern); -        parts.sort(); - -        const actual = match[0]; -        const expected = `/* global${parts.map((v) => `${newline} * ${v}`).join('')}${newline} */`; - -        try { -            assert.strictEqual(actual, expected); -        } catch (e) { -            console.error(`Global declaration error encountered in ${fileName}:`); -            console.error(e.message); -            if (!fix) { -                return false; -            } -        } - -        newSource += source.substring(0, match.index); -        newSource += expected; -        endIndex = match.index + match[0].length; - -        allGlobals.push(...parts); -    } - -    newSource += source.substring(endIndex); - -    // This is an approximate check to see if a global variable is unused. -    // If the global appears in a comment, string, or similar, the check will pass. -    let errorCount = 0; -    for (const global of allGlobals) { -        if (getSubstringCount(newSource, global) <= 1) { -            console.error(`Global variable ${global} appears to be unused in ${fileName}`); -            ++errorCount; -        } -    } - -    if (fix) { -        fs.writeFileSync(fileName, newSource, {encoding: 'utf8'}); -    } - -    return errorCount === 0; -} - - -function main() { -    const fix = (process.argv.length >= 2 && process.argv[2] === '--fix'); -    const directory = path.resolve(__dirname, '..', '..', 'ext'); -    const pattern = /\.js$/; -    const ignorePattern = /^lib[\\/]/; -    const fileNames = getAllFiles(directory, (f) => pattern.test(f) && !ignorePattern.test(f)); -    for (const fileName of fileNames) { -        if (!validateGlobals(path.join(directory, fileName), fix)) { -            process.exit(-1); -            return; -        } -    } -    process.exit(0); -} - - -if (require.main === module) { main(); } diff --git a/dev/lint/html-scripts.js b/dev/lint/html-scripts.js deleted file mode 100644 index db6e6ca4..00000000 --- a/dev/lint/html-scripts.js +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {JSDOM} = require('jsdom'); -const {getAllFiles} = require('../util'); - - -function lstatSyncSafe(fileName) { -    try { -        return fs.lstatSync(fileName); -    } catch (e) { -        return null; -    } -} - -function validatePath(src, fileName, extDir) { -    assert.ok(typeof src === 'string', `<script> missing src attribute in ${fileName}`); -    assert.ok(src.startsWith('/'), `<script> src attribute is not absolute in ${fileName} (src=${JSON.stringify(src)})`); -    const relativeSrc = src.substring(1); -    assert.ok(!path.isAbsolute(relativeSrc), `<script> src attribute is invalid in ${fileName} (src=${JSON.stringify(src)})`); -    const fullSrc = path.join(extDir, relativeSrc); -    const stats = lstatSyncSafe(fullSrc); -    assert.ok(stats !== null, `<script> src file not found in ${fileName} (src=${JSON.stringify(src)})`); -    assert.ok(stats.isFile(), `<script> src file invalid in ${fileName} (src=${JSON.stringify(src)})`); -} - -function getSubstringCount(string, pattern) { -    let count = 0; -    while (true) { -        const match = pattern.exec(string); -        if (match === null) { break; } -        ++count; -    } -    return count; -} - -function getSortedScriptPaths(scriptPaths) { -    // Sort file names without the extension -    const extensionPattern = /\.[^.]*$/; -    scriptPaths = scriptPaths.map((value) => { -        const match = extensionPattern.exec(value); -        let ext = ''; -        if (match !== null) { -            ext = match[0]; -            value = value.substring(0, value.length - ext.length); -        } -        return {value, ext}; -    }); - -    const stringComparer = new Intl.Collator('en-US'); // Invariant locale -    scriptPaths.sort((a, b) => stringComparer.compare(a.value, b.value)); - -    scriptPaths = scriptPaths.map(({value, ext}) => `${value}${ext}`); -    return scriptPaths; -} - -function validateScriptOrder(fileName, window) { -    const {document, Node: {ELEMENT_NODE, TEXT_NODE}, NodeFilter} = window; - -    const scriptElements = document.querySelectorAll('script'); -    if (scriptElements.length === 0) { return; } - -    // Assert all scripts are siblings -    const scriptContainerElement = scriptElements[0].parentNode; -    for (const element of scriptElements) { -        if (element.parentNode !== scriptContainerElement) { -            assert.fail('All script nodes are not contained within the same element'); -        } -    } - -    // Get script groupings and order -    const scriptGroups = []; -    const newlinePattern = /\n/g; -    let separatingText = ''; -    const walker = document.createTreeWalker(scriptContainerElement, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT); -    walker.firstChild(); -    for (let node = walker.currentNode; node !== null; node = walker.nextSibling()) { -        switch (node.nodeType) { -            case ELEMENT_NODE: -                if (node.tagName.toLowerCase() === 'script') { -                    let scriptGroup; -                    if (scriptGroups.length === 0 || getSubstringCount(separatingText, newlinePattern) >= 2) { -                        scriptGroup = []; -                        scriptGroups.push(scriptGroup); -                    } else { -                        scriptGroup = scriptGroups[scriptGroups.length - 1]; -                    } -                    scriptGroup.push(node.src); -                    separatingText = ''; -                } -                break; -            case TEXT_NODE: -                separatingText += node.nodeValue; -                break; -        } -    } - -    // Ensure core.js is first (if it is present) -    const ignorePattern = /^\/lib\//; -    const index = scriptGroups.flat() -        .filter((value) => !ignorePattern.test(value)) -        .findIndex((value) => (value === '/js/core.js')); -    assert.ok(index <= 0, 'core.js is not the first included script'); - -    // Check script order -    for (let i = 0, ii = scriptGroups.length; i < ii; ++i) { -        const scriptGroup = scriptGroups[i]; -        try { -            assert.deepStrictEqual(scriptGroup, getSortedScriptPaths(scriptGroup)); -        } catch (e) { -            console.error(`Script order for group ${i + 1} in file ${fileName} is not correct:`); -            throw e; -        } -    } -} - -function validateHtmlScripts(fileName, extDir) { -    const fullFileName = path.join(extDir, fileName); -    const domSource = fs.readFileSync(fullFileName, {encoding: 'utf8'}); -    const dom = new JSDOM(domSource); -    const {window} = dom; -    const {document} = window; -    try { -        for (const {src} of document.querySelectorAll('script')) { -            validatePath(src, fullFileName, extDir); -        } -        for (const {href} of document.querySelectorAll('link')) { -            validatePath(href, fullFileName, extDir); -        } -        validateScriptOrder(fileName, window); -    } finally { -        window.close(); -    } -} - - -function main() { -    try { -        const extDir = path.resolve(__dirname, '..', '..', 'ext'); -        const pattern = /\.html$/; -        const ignorePattern = /^lib[\\/]/; -        const fileNames = getAllFiles(extDir, (f) => pattern.test(f) && !ignorePattern.test(f)); -        for (const fileName of fileNames) { -            validateHtmlScripts(fileName, extDir); -        } -    } catch (e) { -        console.error(e); -        process.exit(-1); -        return; -    } -    process.exit(0); -} - - -if (require.main === module) { main(); } diff --git a/dev/manifest-util.js b/dev/manifest-util.js index 082cf57c..15175e7f 100644 --- a/dev/manifest-util.js +++ b/dev/manifest-util.js @@ -16,19 +16,21 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const childProcess = require('child_process'); +import childProcess from 'child_process'; +import fs from 'fs'; +import {fileURLToPath} from 'node:url'; +import path from 'path'; +const dirname = path.dirname(fileURLToPath(import.meta.url));  function clone(value) {      return JSON.parse(JSON.stringify(value));  } -class ManifestUtil { +export class ManifestUtil {      constructor() { -        const fileName = path.join(__dirname, 'data', 'manifest-variants.json'); +        const fileName = path.join(dirname, 'data', 'manifest-variants.json');          const {manifest, variants, defaultVariant} = JSON.parse(fs.readFileSync(fileName));          this._manifest = manifest;          this._variants = variants; @@ -74,7 +76,7 @@ class ManifestUtil {      _evaluateModificationCommand(data) {          const {command, args, trim} = data;          const {stdout, stderr, status} = childProcess.spawnSync(command, args, { -            cwd: __dirname, +            cwd: dirname,              stdio: 'pipe',              shell: false          }); @@ -263,7 +265,3 @@ class ManifestUtil {      }  } - -module.exports = { -    ManifestUtil -}; diff --git a/dev/patch-dependencies.js b/dev/patch-dependencies.js deleted file mode 100644 index 81572c5c..00000000 --- a/dev/patch-dependencies.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2021-2022  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/>. - */ - -const fs = require('fs'); -const assert = require('assert'); - -/** - * This function patches the following bug: - * - https://github.com/jsdom/jsdom/issues/3211 - * - https://github.com/dperini/nwsapi/issues/48 - */ -function patchNwsapi() { -    const nwsapiPath = require.resolve('nwsapi'); -    const nwsapiSource = fs.readFileSync(nwsapiPath, {encoding: 'utf8'}); -    const pattern = /(if|while)(\()(?:e&&)?(\(e=e\.parentElement\)\)\{)/g; -    let modifications = 0; -    const nwsapiSourceNew = nwsapiSource.replace(pattern, (g0, g1, g2, g3) => { -        ++modifications; -        return `${g1}${g2}e&&${g3}`; -    }); -    assert.strictEqual(modifications, 2); -    fs.writeFileSync(nwsapiPath, nwsapiSourceNew, {encoding: 'utf8'}); -    // nwsapi is used by JSDOM -    const {testJSDOM} = require('../test/test-jsdom'); -    testJSDOM(); -} - -function main() { -    patchNwsapi(); -} - -if (require.main === module) { main(); } diff --git a/dev/schema-validate.js b/dev/schema-validate.js index 1d7607b7..fbd6b06a 100644 --- a/dev/schema-validate.js +++ b/dev/schema-validate.js @@ -16,21 +16,11 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const {performance} = require('perf_hooks'); -const {VM} = require('./vm'); - -const vm = new VM(); -vm.execute([ -    'js/core.js', -    'js/general/cache-map.js', -    'js/data/json-schema.js' -]); -const JsonSchema = vm.get('JsonSchema'); +import Ajv from 'ajv'; +import {JsonSchema} from '../ext/js/data/json-schema.js';  class JsonSchemaAjv {      constructor(schema) { -        const Ajv = require('ajv');          const ajv = new Ajv({              meta: false,              strictTuples: false, @@ -49,53 +39,9 @@ class JsonSchemaAjv {      }  } -function createJsonSchema(mode, schema) { +export function createJsonSchema(mode, schema) {      switch (mode) {          case 'ajv': return new JsonSchemaAjv(schema);          default: return new JsonSchema(schema);      }  } - -function main() { -    const args = process.argv.slice(2); -    if (args.length < 2) { -        console.log([ -            'Usage:', -            '  node schema-validate [--ajv] <schema-file-name> <data-file-names>...' -        ].join('\n')); -        return; -    } - -    let mode = null; -    if (args[0] === '--ajv') { -        mode = 'ajv'; -        args.splice(0, 1); -    } - -    const schemaSource = fs.readFileSync(args[0], {encoding: 'utf8'}); -    const schema = JSON.parse(schemaSource); - -    for (const dataFileName of args.slice(1)) { -        const start = performance.now(); -        try { -            console.log(`Validating ${dataFileName}...`); -            const dataSource = fs.readFileSync(dataFileName, {encoding: 'utf8'}); -            const data = JSON.parse(dataSource); -            createJsonSchema(mode, schema).validate(data); -            const end = performance.now(); -            console.log(`No issues detected (${((end - start) / 1000).toFixed(2)}s)`); -        } catch (e) { -            const end = performance.now(); -            console.log(`Encountered an error (${((end - start) / 1000).toFixed(2)}s)`); -            console.warn(e); -        } -    } -} - - -if (require.main === module) { main(); } - - -module.exports = { -    createJsonSchema -}; diff --git a/dev/translator-vm.js b/dev/translator-vm.js index 2a51ab8c..9f14523e 100644 --- a/dev/translator-vm.js +++ b/dev/translator-vm.js @@ -16,19 +16,32 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {DatabaseVM, DatabaseVMDictionaryImporterMediaLoader} = require('./database-vm'); -const {createDictionaryArchive} = require('./util'); - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} +import fs from 'fs'; +import url, {fileURLToPath} from 'node:url'; +import path from 'path'; +import {expect, vi} from 'vitest'; +import {AnkiNoteDataCreator} from '../ext/js/data/sandbox/anki-note-data-creator.js'; +import {DictionaryDatabase} from '../ext/js/language/dictionary-database.js'; +import {DictionaryImporterMediaLoader} from '../ext/js/language/dictionary-importer-media-loader.js'; +import {DictionaryImporter} from '../ext/js/language/dictionary-importer.js'; +import {JapaneseUtil} from '../ext/js/language/sandbox/japanese-util.js'; +import {Translator} from '../ext/js/language/translator.js'; +import {createDictionaryArchive} from './util.js'; + +vi.mock('../ext/js/language/dictionary-importer-media-loader.js'); + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +export class TranslatorVM { +    constructor() { +        global.chrome = { +            runtime: { +                getURL: (path2) => { +                    return url.pathToFileURL(path.join(dirname, '..', 'ext', path2.replace(/^\//, ''))).href; +                } +            } +        }; -class TranslatorVM extends DatabaseVM { -    constructor(globals) { -        super(globals);          this._japaneseUtil = null;          this._translator = null;          this._ankiNoteDataCreator = null; @@ -40,43 +53,14 @@ class TranslatorVM extends DatabaseVM {      }      async prepare(dictionaryDirectory, dictionaryName) { -        this.execute([ -            'js/core.js', -            'js/data/sandbox/anki-note-data-creator.js', -            'js/data/database.js', -            'js/data/json-schema.js', -            'js/general/cache-map.js', -            'js/general/regex-util.js', -            'js/general/text-source-map.js', -            'js/language/deinflector.js', -            'js/language/sandbox/dictionary-data-util.js', -            'js/language/dictionary-importer.js', -            'js/language/dictionary-database.js', -            'js/language/sandbox/japanese-util.js', -            'js/language/translator.js', -            'js/media/media-util.js' -        ]); -        const [ -            DictionaryImporter, -            DictionaryDatabase, -            JapaneseUtil, -            Translator, -            AnkiNoteDataCreator -        ] = this.get([ -            'DictionaryImporter', -            'DictionaryDatabase', -            'JapaneseUtil', -            'Translator', -            'AnkiNoteDataCreator' -        ]); -          // Dictionary          this._dictionaryName = dictionaryName;          const testDictionary = createDictionaryArchive(dictionaryDirectory, dictionaryName); +        // const testDictionaryContent = await testDictionary.arrayBuffer();          const testDictionaryContent = await testDictionary.generateAsync({type: 'arraybuffer'});          // Setup database -        const dictionaryImporterMediaLoader = new DatabaseVMDictionaryImporterMediaLoader(); +        const dictionaryImporterMediaLoader = new DictionaryImporterMediaLoader();          const dictionaryImporter = new DictionaryImporter(dictionaryImporterMediaLoader, null);          const dictionaryDatabase = new DictionaryDatabase();          await dictionaryDatabase.prepare(); @@ -87,7 +71,9 @@ class TranslatorVM extends DatabaseVM {              {prefixWildcardsSupported: true}          ); -        assert.deepStrictEqual(errors.length, 0); +        expect(errors.length).toEqual(0); + +        const myDirname = path.dirname(fileURLToPath(import.meta.url));          // Setup translator          this._japaneseUtil = new JapaneseUtil(null); @@ -95,7 +81,7 @@ class TranslatorVM extends DatabaseVM {              japaneseUtil: this._japaneseUtil,              database: dictionaryDatabase          }); -        const deinflectionReasons = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'ext', 'data/deinflect.json'))); +        const deinflectionReasons = JSON.parse(fs.readFileSync(path.join(myDirname, '..', 'ext', 'data/deinflect.json')));          this._translator.prepare(deinflectionReasons);          // Assign properties @@ -132,10 +118,10 @@ class TranslatorVM extends DatabaseVM {                      if (!Object.prototype.hasOwnProperty.call(optionsPresets, entry)) {                          throw new Error('Invalid options preset');                      } -                    Object.assign(options, clone(optionsPresets[entry])); +                    Object.assign(options, structuredClone(optionsPresets[entry]));                      break;                  case 'object': -                    Object.assign(options, clone(entry)); +                    Object.assign(options, structuredClone(entry));                      break;                  default:                      throw new Error('Invalid options type'); @@ -177,7 +163,3 @@ class TranslatorVM extends DatabaseVM {          return options;      }  } - -module.exports = { -    TranslatorVM -}; diff --git a/dev/util.js b/dev/util.js index 65b1d982..cabc40aa 100644 --- a/dev/util.js +++ b/dev/util.js @@ -16,24 +16,11 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); +import fs from 'fs'; +import JSZip from 'jszip'; +import path from 'path'; - -let JSZip = null; - - -function getJSZip() { -    if (JSZip === null) { -        process.noDeprecation = true; // Suppress a warning about JSZip -        JSZip = require(path.join(__dirname, '../ext/lib/jszip.min.js')); -        process.noDeprecation = false; -    } -    return JSZip; -} - - -function getArgs(args, argMap) { +export function getArgs(args, argMap) {      let key = null;      let canKey = true;      let onKey = false; @@ -77,7 +64,7 @@ function getArgs(args, argMap) {      return argMap;  } -function getAllFiles(baseDirectory, predicate=null) { +export function getAllFiles(baseDirectory, predicate=null) {      const results = [];      const directories = [baseDirectory];      while (directories.length > 0) { @@ -99,11 +86,12 @@ function getAllFiles(baseDirectory, predicate=null) {      return results;  } -function createDictionaryArchive(dictionaryDirectory, dictionaryName) { +export function createDictionaryArchive(dictionaryDirectory, dictionaryName) {      const fileNames = fs.readdirSync(dictionaryDirectory); -    const JSZip2 = getJSZip(); -    const archive = new JSZip2(); +    // const zipFileWriter = new BlobWriter(); +    // const zipWriter = new ZipWriter(zipFileWriter); +    const archive = new JSZip();      for (const fileName of fileNames) {          if (/\.json$/.test(fileName)) { @@ -113,17 +101,31 @@ function createDictionaryArchive(dictionaryDirectory, dictionaryName) {                  json.title = dictionaryName;              }              archive.file(fileName, JSON.stringify(json, null, 0)); + +            // await zipWriter.add(fileName, new TextReader(JSON.stringify(json, null, 0)));          } else {              const content = fs.readFileSync(path.join(dictionaryDirectory, fileName), {encoding: null});              archive.file(fileName, content); + +            // console.log('adding'); +            // const r = new TextReader(content); +            // console.log(r.readUint8Array(0, 10)); +            // console.log('reader done'); +            // await zipWriter.add(fileName, r); +            // console.log('??');          }      } +    // await zipWriter.close(); +    // Retrieves the Blob object containing the zip content into `zipFileBlob`. It +    // is also returned by zipWriter.close() for more convenience. +    // const zipFileBlob = await zipFileWriter.getData();      return archive; -} +    // return zipFileBlob; +} -async function testMain(func, ...args) { +export async function testMain(func, ...args) {      try {          await func(...args);      } catch (e) { @@ -131,12 +133,3 @@ async function testMain(func, ...args) {          process.exit(-1);      }  } - - -module.exports = { -    get JSZip() { return getJSZip(); }, -    getArgs, -    getAllFiles, -    createDictionaryArchive, -    testMain -}; diff --git a/dev/vm.js b/dev/vm.js deleted file mode 100644 index c3266443..00000000 --- a/dev/vm.js +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const vm = require('vm'); -const path = require('path'); -const assert = require('assert'); -const crypto = require('crypto'); - - -function getContextEnvironmentRecords(context, names) { -    // Enables export of values from the declarative environment record -    if (!Array.isArray(names) || names.length === 0) { -        return []; -    } - -    let scriptSource = '(() => {\n    "use strict";\n    const results = [];'; -    for (const name of names) { -        scriptSource += `\n    try { results.push(${name}); } catch (e) { results.push(void 0); }`; -    } -    scriptSource += '\n    return results;\n})();'; - -    const script = new vm.Script(scriptSource, {filename: 'getContextEnvironmentRecords'}); - -    const contextHasNames = Object.prototype.hasOwnProperty.call(context, 'names'); -    const contextNames = context.names; -    context.names = names; - -    const results = script.runInContext(context, {}); - -    if (contextHasNames) { -        context.names = contextNames; -    } else { -        delete context.names; -    } - -    return Array.from(results); -} - -function isDeepStrictEqual(val1, val2) { -    if (val1 === val2) { return true; } - -    if (Array.isArray(val1)) { -        if (Array.isArray(val2)) { -            return isArrayDeepStrictEqual(val1, val2); -        } -    } else if (typeof val1 === 'object' && val1 !== null) { -        if (typeof val2 === 'object' && val2 !== null) { -            return isObjectDeepStrictEqual(val1, val2); -        } -    } - -    return false; -} - -function isArrayDeepStrictEqual(val1, val2) { -    const ii = val1.length; -    if (ii !== val2.length) { return false; } - -    for (let i = 0; i < ii; ++i) { -        if (!isDeepStrictEqual(val1[i], val2[i])) { -            return false; -        } -    } - -    return true; -} - -function isObjectDeepStrictEqual(val1, val2) { -    const keys1 = Object.keys(val1); -    const keys2 = Object.keys(val2); - -    if (keys1.length !== keys2.length) { return false; } - -    const keySet = new Set(keys1); -    for (const key of keys2) { -        if (!keySet.delete(key)) { return false; } -    } - -    for (const key of keys1) { -        if (!isDeepStrictEqual(val1[key], val2[key])) { -            return false; -        } -    } - -    const tag1 = Object.prototype.toString.call(val1); -    const tag2 = Object.prototype.toString.call(val2); -    if (tag1 !== tag2) { return false; } - -    return true; -} - -function deepStrictEqual(actual, expected) { -    try { -        // This will fail on prototype === comparison on cross context objects -        assert.deepStrictEqual(actual, expected); -    } catch (e) { -        if (!isDeepStrictEqual(actual, expected)) { -            throw e; -        } -    } -} - - -function createURLClass() { -    const BaseURL = URL; -    const result = function URL(url) { -        const u = new BaseURL(url); -        this.hash = u.hash; -        this.host = u.host; -        this.hostname = u.hostname; -        this.href = u.href; -        this.origin = u.origin; -        this.password = u.password; -        this.pathname = u.pathname; -        this.port = u.port; -        this.protocol = u.protocol; -        this.search = u.search; -        this.searchParams = u.searchParams; -        this.username = u.username; -    }; -    return result; -} - - -class VM { -    constructor(context={}) { -        context.URL = createURLClass(); -        context.crypto = { -            getRandomValues: (array) => { -                const buffer = crypto.randomBytes(array.byteLength); -                buffer.copy(array); -                return array; -            } -        }; -        this._context = vm.createContext(context); -        this._assert = { -            deepStrictEqual -        }; -    } - -    get context() { -        return this._context; -    } - -    get assert() { -        return this._assert; -    } - -    get(names) { -        if (typeof names === 'string') { -            return getContextEnvironmentRecords(this._context, [names])[0]; -        } else if (Array.isArray(names)) { -            return getContextEnvironmentRecords(this._context, names); -        } else { -            throw new Error('Invalid argument'); -        } -    } - -    set(values) { -        if (typeof values === 'object' && values !== null) { -            Object.assign(this._context, values); -        } else { -            throw new Error('Invalid argument'); -        } -    } - -    execute(fileNames) { -        const single = !Array.isArray(fileNames); -        if (single) { -            fileNames = [fileNames]; -        } - -        const results = []; -        for (const fileName of fileNames) { -            const absoluteFileName = path.resolve(__dirname, '..', 'ext', fileName); -            const source = fs.readFileSync(absoluteFileName, {encoding: 'utf8'}); -            const script = new vm.Script(source, {filename: absoluteFileName}); -            results.push(script.runInContext(this._context, {})); -        } - -        return single ? results[0] : results; -    } -} - - -module.exports = { -    VM -}; diff --git a/docs/templates.md b/docs/templates.md index 030ca3d2..e75e4635 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -538,7 +538,7 @@ Returns an array of the mora for a kana string.  ### `typeof` -Returns the type of a value. Use of `#typeof` in the block form may be nonportable. +Returns the type of a value. `#typeof` in the block form will always return `'string'`.  <details>    <summary>Syntax:</summary> diff --git a/ext/js/app/content-script-wrapper.js b/ext/js/app/content-script-wrapper.js index ac921c19..795d3d8b 100644 --- a/ext/js/app/content-script-wrapper.js +++ b/ext/js/app/content-script-wrapper.js @@ -19,6 +19,5 @@  (async () => {      const src = chrome.runtime.getURL('js/app/content-script-main.js');      // eslint-disable-next-line no-unsanitized/method -    const contentMain = await import(src); -    contentMain.main(); +    await import(src);  })(); diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 39e9b5f9..b86b877d 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -1652,6 +1652,8 @@ export class Display extends EventDispatcher {                  Object.assign(result, result2);              }          } + +        console.log(result);      }      _triggerContentClear() { diff --git a/ext/js/language/__mocks__/dictionary-importer-media-loader.js b/ext/js/language/__mocks__/dictionary-importer-media-loader.js new file mode 100644 index 00000000..96f0f6dd --- /dev/null +++ b/ext/js/language/__mocks__/dictionary-importer-media-loader.js @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2021-2022  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/>. + */ + +export class DictionaryImporterMediaLoader { +    async getImageDetails(content) { +        // Placeholder values +        return {content, width: 100, height: 100}; +    } +} diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index bb5ac300..791d1a77 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -17,7 +17,7 @@   */  import * as ajvSchemas from '../../lib/validate-schemas.js'; -import {BlobWriter, TextWriter, Uint8ArrayReader, ZipReader} from '../../lib/zip.js'; +import {BlobWriter, TextWriter, Uint8ArrayReader, ZipReader, configure} from '../../lib/zip.js';  import {stringReverse} from '../core.js';  import {MediaUtil} from '../media/media-util.js';  export class DictionaryImporter { @@ -37,6 +37,13 @@ export class DictionaryImporter {          this._progressReset(); +        configure({ +            workerScripts: { +                deflate: ['../../lib/z-worker.js'], +                inflate: ['../../lib/z-worker.js'] +            } +        }); +          // Read archive          const zipFileReader = new Uint8ArrayReader(new Uint8Array(archiveContent));          const zipReader = new ZipReader(zipFileReader); diff --git a/ext/js/templates/__mocks__/template-renderer-proxy.js b/ext/js/templates/__mocks__/template-renderer-proxy.js new file mode 100644 index 00000000..bcacf13b --- /dev/null +++ b/ext/js/templates/__mocks__/template-renderer-proxy.js @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2021-2022  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/>. + */ + +import {AnkiTemplateRenderer} from '../sandbox/anki-template-renderer.js'; + +export class TemplateRendererProxy { +    constructor() { +        this._preparePromise = null; +        this._ankiTemplateRenderer = new AnkiTemplateRenderer(); +    } + +    async render(template, data, type) { +        await this._prepare(); +        return await this._ankiTemplateRenderer.templateRenderer.render(template, data, type); +    } + +    async renderMulti(items) { +        await this._prepare(); +        return await this._serializeMulti(this._ankiTemplateRenderer.templateRenderer.renderMulti(items)); +    } + +    _prepare() { +        if (this._preparePromise === null) { +            this._preparePromise = this._prepareInternal(); +        } +        return this._preparePromise; +    } + +    async _prepareInternal() { +        await this._ankiTemplateRenderer.prepare(); +    } + +    _serializeError(error) { +        try { +            if (typeof error === 'object' && error !== null) { +                const result = { +                    name: error.name, +                    message: error.message, +                    stack: error.stack +                }; +                if (Object.prototype.hasOwnProperty.call(error, 'data')) { +                    result.data = error.data; +                } +                return result; +            } +        } catch (e) { +            // NOP +        } +        return { +            value: error, +            hasValue: true +        }; +    } + +    _serializeMulti(array) { +        for (let i = 0, ii = array.length; i < ii; ++i) { +            const value = array[i]; +            const {error} = value; +            if (typeof error !== 'undefined') { +                value.error = this._serializeError(error); +            } +        } +        return array; +    } +} diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js index d42402ff..10f69745 100644 --- a/ext/js/templates/sandbox/anki-template-renderer.js +++ b/ext/js/templates/sandbox/anki-template-renderer.js @@ -16,6 +16,7 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +import {Handlebars} from '../../../lib/handlebars.js';  import {AnkiNoteDataCreator} from '../../data/sandbox/anki-note-data-creator.js';  import {PronunciationGenerator} from '../../display/sandbox/pronunciation-generator.js';  import {StructuredContentGenerator} from '../../display/sandbox/structured-content-generator.js'; @@ -26,19 +27,6 @@ import {AnkiTemplateRendererContentManager} from './anki-template-renderer-conte  import {TemplateRendererMediaProvider} from './template-renderer-media-provider.js';  import {TemplateRenderer} from './template-renderer.js'; -/* global - * AnkiNoteDataCreator - * AnkiTemplateRendererContentManager - * CssStyleApplier - * DictionaryDataUtil - * Handlebars - * JapaneseUtil - * PronunciationGenerator - * StructuredContentGenerator - * TemplateRenderer - * TemplateRendererMediaProvider - */ -  /**   * This class contains all Anki-specific template rendering functionality. It is built on   * the generic TemplateRenderer class and various other Anki-related classes. diff --git a/package-lock.json b/package-lock.json index fdd86f41..daddedb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,14 +7,13 @@          "": {              "name": "yomitan",              "version": "0.0.0", -            "hasInstallScript": true,              "license": "GPL-3.0-or-later",              "devDependencies": {                  "@playwright/test": "^1.39.0",                  "@types/node": "^20.8.10", +                "@vitest/coverage-v8": "^0.34.6",                  "@zip.js/zip.js": "^2.7.30",                  "ajv": "^8.12.0", -                "browserify": "^17.0.0",                  "css": "^3.0.0",                  "dexie": "^3.2.4",                  "dexie-export-import": "^4.0.7", @@ -24,6 +23,7 @@                  "eslint-plugin-jsdoc": "^46.8.2",                  "eslint-plugin-jsonc": "^2.10.0",                  "eslint-plugin-no-unsanitized": "^4.0.1", +                "eslint-plugin-vitest": "^0.3.9",                  "fake-indexeddb": "^5.0.1",                  "handlebars": "^4.7.8",                  "html-validate": "^8.7.0", @@ -32,6 +32,7 @@                  "parse5": "^7.1.2",                  "stylelint": "^15.11.0",                  "stylelint-config-recommended": "^13.0.0", +                "vitest": "^0.34.6",                  "wanakana": "^5.2.0"              }          }, @@ -44,35 +45,399 @@                  "node": ">=0.10.0"              }          }, +        "node_modules/@ampproject/remapping": { +            "version": "2.2.1", +            "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", +            "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", +            "dev": true, +            "dependencies": { +                "@jridgewell/gen-mapping": "^0.3.0", +                "@jridgewell/trace-mapping": "^0.3.9" +            }, +            "engines": { +                "node": ">=6.0.0" +            } +        },          "node_modules/@babel/code-frame": { -            "version": "7.16.7", -            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", -            "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", +            "version": "7.22.13", +            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", +            "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", +            "dev": true, +            "dependencies": { +                "@babel/highlight": "^7.22.13", +                "chalk": "^2.4.2" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/ansi-styles": { +            "version": "3.2.1", +            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", +            "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",              "dev": true,              "dependencies": { -                "@babel/highlight": "^7.16.7" +                "color-convert": "^1.9.0" +            }, +            "engines": { +                "node": ">=4" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/chalk": { +            "version": "2.4.2", +            "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", +            "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", +            "dev": true, +            "dependencies": { +                "ansi-styles": "^3.2.1", +                "escape-string-regexp": "^1.0.5", +                "supports-color": "^5.3.0" +            }, +            "engines": { +                "node": ">=4" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/color-convert": { +            "version": "1.9.3", +            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", +            "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", +            "dev": true, +            "dependencies": { +                "color-name": "1.1.3" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/color-name": { +            "version": "1.1.3", +            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", +            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", +            "dev": true +        }, +        "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { +            "version": "1.0.5", +            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", +            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", +            "dev": true, +            "engines": { +                "node": ">=0.8.0" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/has-flag": { +            "version": "3.0.0", +            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", +            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", +            "dev": true, +            "engines": { +                "node": ">=4" +            } +        }, +        "node_modules/@babel/code-frame/node_modules/supports-color": { +            "version": "5.5.0", +            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", +            "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", +            "dev": true, +            "dependencies": { +                "has-flag": "^3.0.0" +            }, +            "engines": { +                "node": ">=4" +            } +        }, +        "node_modules/@babel/compat-data": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", +            "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/core": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", +            "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@ampproject/remapping": "^2.2.0", +                "@babel/code-frame": "^7.22.13", +                "@babel/generator": "^7.23.0", +                "@babel/helper-compilation-targets": "^7.22.15", +                "@babel/helper-module-transforms": "^7.23.0", +                "@babel/helpers": "^7.23.2", +                "@babel/parser": "^7.23.0", +                "@babel/template": "^7.22.15", +                "@babel/traverse": "^7.23.2", +                "@babel/types": "^7.23.0", +                "convert-source-map": "^2.0.0", +                "debug": "^4.1.0", +                "gensync": "^1.0.0-beta.2", +                "json5": "^2.2.3", +                "semver": "^6.3.1" +            }, +            "engines": { +                "node": ">=6.9.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/babel" +            } +        }, +        "node_modules/@babel/core/node_modules/convert-source-map": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +            "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/@babel/core/node_modules/semver": { +            "version": "6.3.1", +            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +            "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "semver": "bin/semver.js" +            } +        }, +        "node_modules/@babel/generator": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", +            "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.23.0", +                "@jridgewell/gen-mapping": "^0.3.2", +                "@jridgewell/trace-mapping": "^0.3.17", +                "jsesc": "^2.5.1" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-compilation-targets": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", +            "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/compat-data": "^7.22.9", +                "@babel/helper-validator-option": "^7.22.15", +                "browserslist": "^4.21.9", +                "lru-cache": "^5.1.1", +                "semver": "^6.3.1" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { +            "version": "5.1.1", +            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", +            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "yallist": "^3.0.2" +            } +        }, +        "node_modules/@babel/helper-compilation-targets/node_modules/semver": { +            "version": "6.3.1", +            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +            "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "semver": "bin/semver.js" +            } +        }, +        "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { +            "version": "3.1.1", +            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", +            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/@babel/helper-environment-visitor": { +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", +            "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-function-name": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", +            "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/template": "^7.22.15", +                "@babel/types": "^7.23.0"              },              "engines": {                  "node": ">=6.9.0"              }          }, +        "node_modules/@babel/helper-hoist-variables": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", +            "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.22.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-module-imports": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", +            "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.22.15" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-module-transforms": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", +            "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-environment-visitor": "^7.22.20", +                "@babel/helper-module-imports": "^7.22.15", +                "@babel/helper-simple-access": "^7.22.5", +                "@babel/helper-split-export-declaration": "^7.22.6", +                "@babel/helper-validator-identifier": "^7.22.20" +            }, +            "engines": { +                "node": ">=6.9.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0" +            } +        }, +        "node_modules/@babel/helper-plugin-utils": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", +            "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-simple-access": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", +            "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.22.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-split-export-declaration": { +            "version": "7.22.6", +            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", +            "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.22.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helper-string-parser": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", +            "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            } +        },          "node_modules/@babel/helper-validator-identifier": { -            "version": "7.16.7", -            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", -            "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", +            "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",              "dev": true,              "engines": {                  "node": ">=6.9.0"              }          }, +        "node_modules/@babel/helper-validator-option": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", +            "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/helpers": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", +            "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/template": "^7.22.15", +                "@babel/traverse": "^7.23.2", +                "@babel/types": "^7.23.0" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        },          "node_modules/@babel/highlight": { -            "version": "7.16.10", -            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", -            "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", +            "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",              "dev": true,              "dependencies": { -                "@babel/helper-validator-identifier": "^7.16.7", -                "chalk": "^2.0.0", +                "@babel/helper-validator-identifier": "^7.22.20", +                "chalk": "^2.4.2",                  "js-tokens": "^4.0.0"              },              "engines": { @@ -117,13 +482,13 @@          "node_modules/@babel/highlight/node_modules/color-name": {              "version": "1.1.3",              "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", -            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", +            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",              "dev": true          },          "node_modules/@babel/highlight/node_modules/escape-string-regexp": {              "version": "1.0.5",              "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", -            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", +            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",              "dev": true,              "engines": {                  "node": ">=0.8.0" @@ -132,7 +497,7 @@          "node_modules/@babel/highlight/node_modules/has-flag": {              "version": "3.0.0",              "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", -            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", +            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",              "dev": true,              "engines": {                  "node": ">=4" @@ -150,6 +515,297 @@                  "node": ">=4"              }          }, +        "node_modules/@babel/parser": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", +            "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "parser": "bin/babel-parser.js" +            }, +            "engines": { +                "node": ">=6.0.0" +            } +        }, +        "node_modules/@babel/plugin-syntax-async-generators": { +            "version": "7.8.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", +            "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-bigint": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", +            "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-class-properties": { +            "version": "7.12.13", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", +            "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.12.13" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-import-meta": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", +            "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.10.4" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-json-strings": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", +            "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-jsx": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", +            "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.22.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-logical-assignment-operators": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", +            "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.10.4" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", +            "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-numeric-separator": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", +            "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.10.4" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-object-rest-spread": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", +            "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-optional-catch-binding": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", +            "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-optional-chaining": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", +            "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.8.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-top-level-await": { +            "version": "7.14.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", +            "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.14.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/plugin-syntax-typescript": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", +            "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-plugin-utils": "^7.22.5" +            }, +            "engines": { +                "node": ">=6.9.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0-0" +            } +        }, +        "node_modules/@babel/template": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", +            "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/code-frame": "^7.22.13", +                "@babel/parser": "^7.22.15", +                "@babel/types": "^7.22.15" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/traverse": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", +            "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/code-frame": "^7.22.13", +                "@babel/generator": "^7.23.0", +                "@babel/helper-environment-visitor": "^7.22.20", +                "@babel/helper-function-name": "^7.23.0", +                "@babel/helper-hoist-variables": "^7.22.5", +                "@babel/helper-split-export-declaration": "^7.22.6", +                "@babel/parser": "^7.23.0", +                "@babel/types": "^7.23.0", +                "debug": "^4.1.0", +                "globals": "^11.1.0" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@babel/traverse/node_modules/globals": { +            "version": "11.12.0", +            "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", +            "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=4" +            } +        }, +        "node_modules/@babel/types": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", +            "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/helper-string-parser": "^7.22.5", +                "@babel/helper-validator-identifier": "^7.22.20", +                "to-fast-properties": "^2.0.0" +            }, +            "engines": { +                "node": ">=6.9.0" +            } +        }, +        "node_modules/@bcoe/v8-coverage": { +            "version": "0.2.3", +            "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", +            "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", +            "dev": true +        },          "node_modules/@csstools/css-parser-algorithms": {              "version": "2.3.1",              "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz", @@ -603,9 +1259,9 @@              }          },          "node_modules/@eslint-community/eslint-utils": { -            "version": "4.3.0", -            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", -            "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", +            "version": "4.4.0", +            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", +            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",              "dev": true,              "dependencies": {                  "eslint-visitor-keys": "^3.3.0" @@ -792,6 +1448,516 @@                  "url": "https://github.com/chalk/strip-ansi?sponsor=1"              }          }, +        "node_modules/@istanbuljs/load-nyc-config": { +            "version": "1.1.0", +            "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", +            "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "camelcase": "^5.3.1", +                "find-up": "^4.1.0", +                "get-package-type": "^0.1.0", +                "js-yaml": "^3.13.1", +                "resolve-from": "^5.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { +            "version": "1.0.10", +            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", +            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "sprintf-js": "~1.0.2" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { +            "version": "5.3.1", +            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", +            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { +            "version": "4.1.0", +            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", +            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "locate-path": "^5.0.0", +                "path-exists": "^4.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { +            "version": "3.14.1", +            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", +            "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "argparse": "^1.0.7", +                "esprima": "^4.0.0" +            }, +            "bin": { +                "js-yaml": "bin/js-yaml.js" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { +            "version": "5.0.0", +            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", +            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-locate": "^4.1.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { +            "version": "2.3.0", +            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", +            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-try": "^2.0.0" +            }, +            "engines": { +                "node": ">=6" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { +            "version": "4.1.0", +            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", +            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-limit": "^2.2.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { +            "version": "5.0.0", +            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", +            "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@istanbuljs/schema": { +            "version": "0.1.3", +            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", +            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", +            "dev": true, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/@jest/console": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", +            "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "slash": "^3.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/core": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", +            "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/console": "^29.7.0", +                "@jest/reporters": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "ansi-escapes": "^4.2.1", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "exit": "^0.1.2", +                "graceful-fs": "^4.2.9", +                "jest-changed-files": "^29.7.0", +                "jest-config": "^29.7.0", +                "jest-haste-map": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-resolve-dependencies": "^29.7.0", +                "jest-runner": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "jest-watcher": "^29.7.0", +                "micromatch": "^4.0.4", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "strip-ansi": "^6.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" +            }, +            "peerDependenciesMeta": { +                "node-notifier": { +                    "optional": true +                } +            } +        }, +        "node_modules/@jest/environment": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", +            "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/fake-timers": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-mock": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/expect": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", +            "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "expect": "^29.7.0", +                "jest-snapshot": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/expect-utils": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", +            "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "jest-get-type": "^29.6.3" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/fake-timers": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", +            "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "@sinonjs/fake-timers": "^10.0.2", +                "@types/node": "*", +                "jest-message-util": "^29.7.0", +                "jest-mock": "^29.7.0", +                "jest-util": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/globals": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", +            "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/environment": "^29.7.0", +                "@jest/expect": "^29.7.0", +                "@jest/types": "^29.6.3", +                "jest-mock": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/reporters": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", +            "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@bcoe/v8-coverage": "^0.2.3", +                "@jest/console": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@jridgewell/trace-mapping": "^0.3.18", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "collect-v8-coverage": "^1.0.0", +                "exit": "^0.1.2", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "istanbul-lib-coverage": "^3.0.0", +                "istanbul-lib-instrument": "^6.0.0", +                "istanbul-lib-report": "^3.0.0", +                "istanbul-lib-source-maps": "^4.0.0", +                "istanbul-reports": "^3.1.3", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-worker": "^29.7.0", +                "slash": "^3.0.0", +                "string-length": "^4.0.1", +                "strip-ansi": "^6.0.0", +                "v8-to-istanbul": "^9.0.1" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" +            }, +            "peerDependenciesMeta": { +                "node-notifier": { +                    "optional": true +                } +            } +        }, +        "node_modules/@jest/schemas": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", +            "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", +            "dev": true, +            "dependencies": { +                "@sinclair/typebox": "^0.27.8" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/source-map": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", +            "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jridgewell/trace-mapping": "^0.3.18", +                "callsites": "^3.0.0", +                "graceful-fs": "^4.2.9" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/test-result": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", +            "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/console": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/istanbul-lib-coverage": "^2.0.0", +                "collect-v8-coverage": "^1.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/test-sequencer": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", +            "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/test-result": "^29.7.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "slash": "^3.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/transform": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", +            "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/core": "^7.11.6", +                "@jest/types": "^29.6.3", +                "@jridgewell/trace-mapping": "^0.3.18", +                "babel-plugin-istanbul": "^6.1.1", +                "chalk": "^4.0.0", +                "convert-source-map": "^2.0.0", +                "fast-json-stable-stringify": "^2.1.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-util": "^29.7.0", +                "micromatch": "^4.0.4", +                "pirates": "^4.0.4", +                "slash": "^3.0.0", +                "write-file-atomic": "^4.0.2" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jest/transform/node_modules/convert-source-map": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +            "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/@jest/transform/node_modules/write-file-atomic": { +            "version": "4.0.2", +            "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", +            "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "imurmurhash": "^0.1.4", +                "signal-exit": "^3.0.7" +            }, +            "engines": { +                "node": "^12.13.0 || ^14.15.0 || >=16.0.0" +            } +        }, +        "node_modules/@jest/types": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", +            "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/schemas": "^29.6.3", +                "@types/istanbul-lib-coverage": "^2.0.0", +                "@types/istanbul-reports": "^3.0.0", +                "@types/node": "*", +                "@types/yargs": "^17.0.8", +                "chalk": "^4.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/@jridgewell/gen-mapping": { +            "version": "0.3.3", +            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", +            "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", +            "dev": true, +            "dependencies": { +                "@jridgewell/set-array": "^1.0.1", +                "@jridgewell/sourcemap-codec": "^1.4.10", +                "@jridgewell/trace-mapping": "^0.3.9" +            }, +            "engines": { +                "node": ">=6.0.0" +            } +        }, +        "node_modules/@jridgewell/resolve-uri": { +            "version": "3.1.1", +            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", +            "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", +            "dev": true, +            "engines": { +                "node": ">=6.0.0" +            } +        }, +        "node_modules/@jridgewell/set-array": { +            "version": "1.1.2", +            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", +            "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", +            "dev": true, +            "engines": { +                "node": ">=6.0.0" +            } +        }, +        "node_modules/@jridgewell/sourcemap-codec": { +            "version": "1.4.15", +            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", +            "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", +            "dev": true +        }, +        "node_modules/@jridgewell/trace-mapping": { +            "version": "0.3.20", +            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", +            "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", +            "dev": true, +            "dependencies": { +                "@jridgewell/resolve-uri": "^3.1.0", +                "@jridgewell/sourcemap-codec": "^1.4.14" +            } +        },          "node_modules/@nodelib/fs.scandir": {              "version": "2.1.5",              "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -868,6 +2034,34 @@                  "ajv": "4.11.8 - 8"              }          }, +        "node_modules/@sinclair/typebox": { +            "version": "0.27.8", +            "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", +            "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", +            "dev": true +        }, +        "node_modules/@sinonjs/commons": { +            "version": "3.0.0", +            "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", +            "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "type-detect": "4.0.8" +            } +        }, +        "node_modules/@sinonjs/fake-timers": { +            "version": "10.3.0", +            "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", +            "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@sinonjs/commons": "^3.0.0" +            } +        },          "node_modules/@tootallnate/once": {              "version": "2.0.0",              "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -877,6 +2071,115 @@                  "node": ">= 10"              }          }, +        "node_modules/@types/babel__core": { +            "version": "7.20.3", +            "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", +            "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/parser": "^7.20.7", +                "@babel/types": "^7.20.7", +                "@types/babel__generator": "*", +                "@types/babel__template": "*", +                "@types/babel__traverse": "*" +            } +        }, +        "node_modules/@types/babel__generator": { +            "version": "7.6.6", +            "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", +            "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.0.0" +            } +        }, +        "node_modules/@types/babel__template": { +            "version": "7.4.3", +            "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", +            "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/parser": "^7.1.0", +                "@babel/types": "^7.0.0" +            } +        }, +        "node_modules/@types/babel__traverse": { +            "version": "7.20.3", +            "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", +            "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/types": "^7.20.7" +            } +        }, +        "node_modules/@types/chai": { +            "version": "4.3.9", +            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", +            "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", +            "dev": true +        }, +        "node_modules/@types/chai-subset": { +            "version": "1.3.5", +            "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", +            "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", +            "dev": true, +            "dependencies": { +                "@types/chai": "*" +            } +        }, +        "node_modules/@types/graceful-fs": { +            "version": "4.1.8", +            "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", +            "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@types/node": "*" +            } +        }, +        "node_modules/@types/istanbul-lib-coverage": { +            "version": "2.0.5", +            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", +            "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", +            "dev": true +        }, +        "node_modules/@types/istanbul-lib-report": { +            "version": "3.0.2", +            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", +            "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@types/istanbul-lib-coverage": "*" +            } +        }, +        "node_modules/@types/istanbul-reports": { +            "version": "3.0.3", +            "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", +            "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@types/istanbul-lib-report": "*" +            } +        }, +        "node_modules/@types/json-schema": { +            "version": "7.0.14", +            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", +            "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", +            "dev": true +        },          "node_modules/@types/minimist": {              "version": "1.2.2",              "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -898,12 +2201,165 @@              "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",              "dev": true          }, +        "node_modules/@types/semver": { +            "version": "7.5.4", +            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", +            "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", +            "dev": true +        }, +        "node_modules/@types/stack-utils": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", +            "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/@types/yargs": { +            "version": "17.0.29", +            "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", +            "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@types/yargs-parser": "*" +            } +        }, +        "node_modules/@types/yargs-parser": { +            "version": "21.0.2", +            "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", +            "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "node_modules/@ungap/structured-clone": {              "version": "1.2.0",              "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",              "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",              "dev": true          }, +        "node_modules/@vitest/coverage-v8": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-0.34.6.tgz", +            "integrity": "sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==", +            "dev": true, +            "dependencies": { +                "@ampproject/remapping": "^2.2.1", +                "@bcoe/v8-coverage": "^0.2.3", +                "istanbul-lib-coverage": "^3.2.0", +                "istanbul-lib-report": "^3.0.1", +                "istanbul-lib-source-maps": "^4.0.1", +                "istanbul-reports": "^3.1.5", +                "magic-string": "^0.30.1", +                "picocolors": "^1.0.0", +                "std-env": "^3.3.3", +                "test-exclude": "^6.0.0", +                "v8-to-istanbul": "^9.1.0" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            }, +            "peerDependencies": { +                "vitest": ">=0.32.0 <1" +            } +        }, +        "node_modules/@vitest/expect": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", +            "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", +            "dev": true, +            "dependencies": { +                "@vitest/spy": "0.34.6", +                "@vitest/utils": "0.34.6", +                "chai": "^4.3.10" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        }, +        "node_modules/@vitest/runner": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", +            "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", +            "dev": true, +            "dependencies": { +                "@vitest/utils": "0.34.6", +                "p-limit": "^4.0.0", +                "pathe": "^1.1.1" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        }, +        "node_modules/@vitest/runner/node_modules/p-limit": { +            "version": "4.0.0", +            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", +            "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", +            "dev": true, +            "dependencies": { +                "yocto-queue": "^1.0.0" +            }, +            "engines": { +                "node": "^12.20.0 || ^14.13.1 || >=16.0.0" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/@vitest/runner/node_modules/yocto-queue": { +            "version": "1.0.0", +            "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", +            "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", +            "dev": true, +            "engines": { +                "node": ">=12.20" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/@vitest/snapshot": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", +            "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", +            "dev": true, +            "dependencies": { +                "magic-string": "^0.30.1", +                "pathe": "^1.1.1", +                "pretty-format": "^29.5.0" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        }, +        "node_modules/@vitest/spy": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", +            "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", +            "dev": true, +            "dependencies": { +                "tinyspy": "^2.1.1" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        }, +        "node_modules/@vitest/utils": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", +            "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", +            "dev": true, +            "dependencies": { +                "diff-sequences": "^29.4.3", +                "loupe": "^2.3.6", +                "pretty-format": "^29.5.0" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        },          "node_modules/@zip.js/zip.js": {              "version": "2.7.30",              "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.30.tgz", @@ -942,33 +2398,10 @@                  "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"              }          }, -        "node_modules/acorn-node": { -            "version": "1.8.2", -            "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", -            "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", -            "dev": true, -            "dependencies": { -                "acorn": "^7.0.0", -                "acorn-walk": "^7.0.0", -                "xtend": "^4.0.2" -            } -        }, -        "node_modules/acorn-node/node_modules/acorn": { -            "version": "7.4.1", -            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", -            "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", -            "dev": true, -            "bin": { -                "acorn": "bin/acorn" -            }, -            "engines": { -                "node": ">=0.4.0" -            } -        }, -        "node_modules/acorn-node/node_modules/acorn-walk": { -            "version": "7.2.0", -            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", -            "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", +        "node_modules/acorn-walk": { +            "version": "8.3.0", +            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", +            "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==",              "dev": true,              "engines": {                  "node": ">=0.4.0" @@ -1002,6 +2435,37 @@                  "url": "https://github.com/sponsors/epoberezkin"              }          }, +        "node_modules/ansi-escapes": { +            "version": "4.3.2", +            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", +            "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "type-fest": "^0.21.3" +            }, +            "engines": { +                "node": ">=8" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/ansi-escapes/node_modules/type-fest": { +            "version": "0.21.3", +            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", +            "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        },          "node_modules/ansi-regex": {              "version": "5.0.1",              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1026,6 +2490,21 @@                  "url": "https://github.com/chalk/ansi-styles?sponsor=1"              }          }, +        "node_modules/anymatch": { +            "version": "3.1.3", +            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", +            "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "normalize-path": "^3.0.0", +                "picomatch": "^2.0.4" +            }, +            "engines": { +                "node": ">= 8" +            } +        },          "node_modules/are-docs-informative": {              "version": "0.0.2",              "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -1059,47 +2538,13 @@                  "node": ">=0.10.0"              }          }, -        "node_modules/asn1.js": { -            "version": "5.4.1", -            "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", -            "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", -            "dev": true, -            "dependencies": { -                "bn.js": "^4.0.0", -                "inherits": "^2.0.1", -                "minimalistic-assert": "^1.0.0", -                "safer-buffer": "^2.1.0" -            } -        }, -        "node_modules/asn1.js/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true -        }, -        "node_modules/assert": { -            "version": "1.5.0", -            "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", -            "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", -            "dev": true, -            "dependencies": { -                "object-assign": "^4.1.1", -                "util": "0.10.3" -            } -        }, -        "node_modules/assert/node_modules/inherits": { -            "version": "2.0.1", -            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", -            "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", -            "dev": true -        }, -        "node_modules/assert/node_modules/util": { -            "version": "0.10.3", -            "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", -            "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", +        "node_modules/assertion-error": { +            "version": "1.1.0", +            "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", +            "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",              "dev": true, -            "dependencies": { -                "inherits": "2.0.1" +            "engines": { +                "node": "*"              }          },          "node_modules/astral-regex": { @@ -1129,277 +2574,216 @@                  "node": ">= 4.5.0"              }          }, -        "node_modules/available-typed-arrays": { -            "version": "1.0.5", -            "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", -            "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", +        "node_modules/babel-jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", +            "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",              "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/transform": "^29.7.0", +                "@types/babel__core": "^7.1.14", +                "babel-plugin-istanbul": "^6.1.1", +                "babel-preset-jest": "^29.6.3", +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "slash": "^3.0.0" +            },              "engines": { -                "node": ">= 0.4" +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0"              }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +            "peerDependencies": { +                "@babel/core": "^7.8.0"              }          }, -        "node_modules/balanced-match": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", -            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", -            "dev": true -        }, -        "node_modules/base64-js": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", -            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", -            "dev": true, -            "funding": [ -                { -                    "type": "github", -                    "url": "https://github.com/sponsors/feross" -                }, -                { -                    "type": "patreon", -                    "url": "https://www.patreon.com/feross" -                }, -                { -                    "type": "consulting", -                    "url": "https://feross.org/support" -                } -            ] -        }, -        "node_modules/bn.js": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", -            "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", -            "dev": true -        }, -        "node_modules/brace-expansion": { -            "version": "1.1.11", -            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", -            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", +        "node_modules/babel-plugin-istanbul": { +            "version": "6.1.1", +            "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", +            "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "balanced-match": "^1.0.0", -                "concat-map": "0.0.1" +                "@babel/helper-plugin-utils": "^7.0.0", +                "@istanbuljs/load-nyc-config": "^1.0.0", +                "@istanbuljs/schema": "^0.1.2", +                "istanbul-lib-instrument": "^5.0.4", +                "test-exclude": "^6.0.0" +            }, +            "engines": { +                "node": ">=8"              }          }, -        "node_modules/braces": { -            "version": "3.0.2", -            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", -            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", +        "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { +            "version": "5.2.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", +            "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "fill-range": "^7.0.1" +                "@babel/core": "^7.12.3", +                "@babel/parser": "^7.14.7", +                "@istanbuljs/schema": "^0.1.2", +                "istanbul-lib-coverage": "^3.2.0", +                "semver": "^6.3.0"              },              "engines": {                  "node": ">=8"              }          }, -        "node_modules/brorand": { -            "version": "1.1.0", -            "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", -            "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", -            "dev": true -        }, -        "node_modules/browser-pack": { -            "version": "6.1.0", -            "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", -            "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", +        "node_modules/babel-plugin-istanbul/node_modules/semver": { +            "version": "6.3.1", +            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +            "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",              "dev": true, -            "dependencies": { -                "combine-source-map": "~0.8.0", -                "defined": "^1.0.0", -                "JSONStream": "^1.0.3", -                "safe-buffer": "^5.1.1", -                "through2": "^2.0.0", -                "umd": "^3.0.0" -            }, +            "optional": true, +            "peer": true,              "bin": { -                "browser-pack": "bin/cmd.js" +                "semver": "bin/semver.js"              }          }, -        "node_modules/browser-resolve": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", -            "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", -            "dev": true, -            "dependencies": { -                "resolve": "^1.17.0" -            } -        }, -        "node_modules/browserify": { -            "version": "17.0.0", -            "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", -            "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", -            "dev": true, -            "dependencies": { -                "assert": "^1.4.0", -                "browser-pack": "^6.0.1", -                "browser-resolve": "^2.0.0", -                "browserify-zlib": "~0.2.0", -                "buffer": "~5.2.1", -                "cached-path-relative": "^1.0.0", -                "concat-stream": "^1.6.0", -                "console-browserify": "^1.1.0", -                "constants-browserify": "~1.0.0", -                "crypto-browserify": "^3.0.0", -                "defined": "^1.0.0", -                "deps-sort": "^2.0.1", -                "domain-browser": "^1.2.0", -                "duplexer2": "~0.1.2", -                "events": "^3.0.0", -                "glob": "^7.1.0", -                "has": "^1.0.0", -                "htmlescape": "^1.1.0", -                "https-browserify": "^1.0.0", -                "inherits": "~2.0.1", -                "insert-module-globals": "^7.2.1", -                "JSONStream": "^1.0.3", -                "labeled-stream-splicer": "^2.0.0", -                "mkdirp-classic": "^0.5.2", -                "module-deps": "^6.2.3", -                "os-browserify": "~0.3.0", -                "parents": "^1.0.1", -                "path-browserify": "^1.0.0", -                "process": "~0.11.0", -                "punycode": "^1.3.2", -                "querystring-es3": "~0.2.0", -                "read-only-stream": "^2.0.0", -                "readable-stream": "^2.0.2", -                "resolve": "^1.1.4", -                "shasum-object": "^1.0.0", -                "shell-quote": "^1.6.1", -                "stream-browserify": "^3.0.0", -                "stream-http": "^3.0.0", -                "string_decoder": "^1.1.1", -                "subarg": "^1.0.0", -                "syntax-error": "^1.1.1", -                "through2": "^2.0.0", -                "timers-browserify": "^1.0.1", -                "tty-browserify": "0.0.1", -                "url": "~0.11.0", -                "util": "~0.12.0", -                "vm-browserify": "^1.0.0", -                "xtend": "^4.0.0" -            }, -            "bin": { -                "browserify": "bin/cmd.js" +        "node_modules/babel-plugin-jest-hoist": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", +            "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/template": "^7.3.3", +                "@babel/types": "^7.3.3", +                "@types/babel__core": "^7.1.14", +                "@types/babel__traverse": "^7.0.6"              },              "engines": { -                "node": ">= 0.8" +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0"              }          }, -        "node_modules/browserify-aes": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", -            "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", +        "node_modules/babel-preset-current-node-syntax": { +            "version": "1.0.1", +            "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", +            "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",              "dev": true, -            "dependencies": { -                "buffer-xor": "^1.0.3", -                "cipher-base": "^1.0.0", -                "create-hash": "^1.1.0", -                "evp_bytestokey": "^1.0.3", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/plugin-syntax-async-generators": "^7.8.4", +                "@babel/plugin-syntax-bigint": "^7.8.3", +                "@babel/plugin-syntax-class-properties": "^7.8.3", +                "@babel/plugin-syntax-import-meta": "^7.8.3", +                "@babel/plugin-syntax-json-strings": "^7.8.3", +                "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", +                "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", +                "@babel/plugin-syntax-numeric-separator": "^7.8.3", +                "@babel/plugin-syntax-object-rest-spread": "^7.8.3", +                "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", +                "@babel/plugin-syntax-optional-chaining": "^7.8.3", +                "@babel/plugin-syntax-top-level-await": "^7.8.3" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0"              }          }, -        "node_modules/browserify-cipher": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", -            "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", +        "node_modules/babel-preset-jest": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", +            "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "browserify-aes": "^1.0.4", -                "browserify-des": "^1.0.0", -                "evp_bytestokey": "^1.0.0" +                "babel-plugin-jest-hoist": "^29.6.3", +                "babel-preset-current-node-syntax": "^1.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "@babel/core": "^7.0.0"              }          }, -        "node_modules/browserify-des": { +        "node_modules/balanced-match": {              "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", -            "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", -            "dev": true, -            "dependencies": { -                "cipher-base": "^1.0.1", -                "des.js": "^1.0.0", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.1.2" -            } +            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", +            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", +            "dev": true          }, -        "node_modules/browserify-rsa": { -            "version": "4.1.0", -            "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", -            "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", +        "node_modules/brace-expansion": { +            "version": "1.1.11", +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", +            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",              "dev": true,              "dependencies": { -                "bn.js": "^5.0.0", -                "randombytes": "^2.0.1" +                "balanced-match": "^1.0.0", +                "concat-map": "0.0.1"              }          }, -        "node_modules/browserify-sign": { -            "version": "4.2.2", -            "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", -            "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", +        "node_modules/braces": { +            "version": "3.0.2", +            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", +            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",              "dev": true,              "dependencies": { -                "bn.js": "^5.2.1", -                "browserify-rsa": "^4.1.0", -                "create-hash": "^1.2.0", -                "create-hmac": "^1.1.7", -                "elliptic": "^6.5.4", -                "inherits": "^2.0.4", -                "parse-asn1": "^5.1.6", -                "readable-stream": "^3.6.2", -                "safe-buffer": "^5.2.1" +                "fill-range": "^7.0.1"              },              "engines": { -                "node": ">= 4" +                "node": ">=8"              }          }, -        "node_modules/browserify-sign/node_modules/readable-stream": { -            "version": "3.6.2", -            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", -            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", +        "node_modules/browserslist": { +            "version": "4.22.1", +            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", +            "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",              "dev": true, +            "funding": [ +                { +                    "type": "opencollective", +                    "url": "https://opencollective.com/browserslist" +                }, +                { +                    "type": "tidelift", +                    "url": "https://tidelift.com/funding/github/npm/browserslist" +                }, +                { +                    "type": "github", +                    "url": "https://github.com/sponsors/ai" +                } +            ], +            "optional": true, +            "peer": true,              "dependencies": { -                "inherits": "^2.0.3", -                "string_decoder": "^1.1.1", -                "util-deprecate": "^1.0.1" +                "caniuse-lite": "^1.0.30001541", +                "electron-to-chromium": "^1.4.535", +                "node-releases": "^2.0.13", +                "update-browserslist-db": "^1.0.13" +            }, +            "bin": { +                "browserslist": "cli.js"              },              "engines": { -                "node": ">= 6" +                "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"              }          }, -        "node_modules/browserify-zlib": { -            "version": "0.2.0", -            "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", -            "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", -            "dev": true, -            "dependencies": { -                "pako": "~1.0.5" -            } -        }, -        "node_modules/buffer": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", -            "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", +        "node_modules/bser": { +            "version": "2.1.1", +            "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", +            "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "base64-js": "^1.0.2", -                "ieee754": "^1.1.4" +                "node-int64": "^0.4.0"              }          },          "node_modules/buffer-from": {              "version": "1.1.2",              "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",              "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", -            "dev": true -        }, -        "node_modules/buffer-xor": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", -            "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", -            "dev": true +            "dev": true, +            "optional": true, +            "peer": true          },          "node_modules/builtin-modules": {              "version": "3.3.0", @@ -1413,29 +2797,13 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/builtin-status-codes": { -            "version": "3.0.0", -            "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", -            "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", -            "dev": true -        }, -        "node_modules/cached-path-relative": { -            "version": "1.1.0", -            "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", -            "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", -            "dev": true -        }, -        "node_modules/call-bind": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", -            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", +        "node_modules/cac": { +            "version": "6.7.14", +            "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", +            "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",              "dev": true, -            "dependencies": { -                "function-bind": "^1.1.1", -                "get-intrinsic": "^1.0.2" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +            "engines": { +                "node": ">=8"              }          },          "node_modules/callsites": { @@ -1489,6 +2857,46 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, +        "node_modules/caniuse-lite": { +            "version": "1.0.30001561", +            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", +            "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", +            "dev": true, +            "funding": [ +                { +                    "type": "opencollective", +                    "url": "https://opencollective.com/browserslist" +                }, +                { +                    "type": "tidelift", +                    "url": "https://tidelift.com/funding/github/npm/caniuse-lite" +                }, +                { +                    "type": "github", +                    "url": "https://github.com/sponsors/ai" +                } +            ], +            "optional": true, +            "peer": true +        }, +        "node_modules/chai": { +            "version": "4.3.10", +            "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", +            "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", +            "dev": true, +            "dependencies": { +                "assertion-error": "^1.1.0", +                "check-error": "^1.0.3", +                "deep-eql": "^4.1.3", +                "get-func-name": "^2.0.2", +                "loupe": "^2.3.6", +                "pathval": "^1.1.1", +                "type-detect": "^4.0.8" +            }, +            "engines": { +                "node": ">=4" +            } +        },          "node_modules/chalk": {              "version": "4.1.2",              "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1505,16 +2913,109 @@                  "url": "https://github.com/chalk/chalk?sponsor=1"              }          }, -        "node_modules/cipher-base": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", -            "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", +        "node_modules/char-regex": { +            "version": "1.0.2", +            "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", +            "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=10" +            } +        }, +        "node_modules/check-error": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", +            "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", +            "dev": true, +            "dependencies": { +                "get-func-name": "^2.0.2" +            }, +            "engines": { +                "node": "*" +            } +        }, +        "node_modules/ci-info": { +            "version": "3.9.0", +            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", +            "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", +            "dev": true, +            "funding": [ +                { +                    "type": "github", +                    "url": "https://github.com/sponsors/sibiraj-s" +                } +            ], +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/cjs-module-lexer": { +            "version": "1.2.3", +            "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", +            "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/cliui": { +            "version": "8.0.1", +            "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", +            "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" +                "string-width": "^4.2.0", +                "strip-ansi": "^6.0.1", +                "wrap-ansi": "^7.0.0" +            }, +            "engines": { +                "node": ">=12"              }          }, +        "node_modules/cliui/node_modules/wrap-ansi": { +            "version": "7.0.0", +            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", +            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "ansi-styles": "^4.0.0", +                "string-width": "^4.1.0", +                "strip-ansi": "^6.0.0" +            }, +            "engines": { +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/chalk/wrap-ansi?sponsor=1" +            } +        }, +        "node_modules/co": { +            "version": "4.6.0", +            "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", +            "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "iojs": ">= 1.0.0", +                "node": ">= 0.12.0" +            } +        }, +        "node_modules/collect-v8-coverage": { +            "version": "1.0.2", +            "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", +            "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "node_modules/color-convert": {              "version": "2.0.1",              "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1539,18 +3040,6 @@              "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",              "dev": true          }, -        "node_modules/combine-source-map": { -            "version": "0.8.0", -            "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", -            "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", -            "dev": true, -            "dependencies": { -                "convert-source-map": "~1.1.0", -                "inline-source-map": "~0.6.0", -                "lodash.memoize": "~3.0.3", -                "source-map": "~0.5.3" -            } -        },          "node_modules/combined-stream": {              "version": "1.0.8",              "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1578,39 +3067,6 @@              "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",              "dev": true          }, -        "node_modules/concat-stream": { -            "version": "1.6.2", -            "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", -            "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", -            "dev": true, -            "engines": [ -                "node >= 0.8" -            ], -            "dependencies": { -                "buffer-from": "^1.0.0", -                "inherits": "^2.0.3", -                "readable-stream": "^2.2.2", -                "typedarray": "^0.0.6" -            } -        }, -        "node_modules/console-browserify": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", -            "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", -            "dev": true -        }, -        "node_modules/constants-browserify": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", -            "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", -            "dev": true -        }, -        "node_modules/convert-source-map": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", -            "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", -            "dev": true -        },          "node_modules/core-util-is": {              "version": "1.0.3",              "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -1635,47 +3091,27 @@                  "url": "https://github.com/sponsors/d-fischer"              }          }, -        "node_modules/create-ecdh": { -            "version": "4.0.4", -            "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", -            "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", -            "dev": true, -            "dependencies": { -                "bn.js": "^4.1.0", -                "elliptic": "^6.5.3" -            } -        }, -        "node_modules/create-ecdh/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true -        }, -        "node_modules/create-hash": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", -            "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", -            "dev": true, -            "dependencies": { -                "cipher-base": "^1.0.1", -                "inherits": "^2.0.1", -                "md5.js": "^1.3.4", -                "ripemd160": "^2.0.1", -                "sha.js": "^2.4.0" -            } -        }, -        "node_modules/create-hmac": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", -            "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", +        "node_modules/create-jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", +            "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "cipher-base": "^1.0.3", -                "create-hash": "^1.1.0", -                "inherits": "^2.0.1", -                "ripemd160": "^2.0.0", -                "safe-buffer": "^5.0.1", -                "sha.js": "^2.4.8" +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "exit": "^0.1.2", +                "graceful-fs": "^4.2.9", +                "jest-config": "^29.7.0", +                "jest-util": "^29.7.0", +                "prompts": "^2.0.1" +            }, +            "bin": { +                "create-jest": "bin/create-jest.js" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0"              }          },          "node_modules/cross-spawn": { @@ -1692,28 +3128,6 @@                  "node": ">= 8"              }          }, -        "node_modules/crypto-browserify": { -            "version": "3.12.0", -            "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", -            "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", -            "dev": true, -            "dependencies": { -                "browserify-cipher": "^1.0.0", -                "browserify-sign": "^4.0.0", -                "create-ecdh": "^4.0.0", -                "create-hash": "^1.1.0", -                "create-hmac": "^1.1.0", -                "diffie-hellman": "^5.0.0", -                "inherits": "^2.0.1", -                "pbkdf2": "^3.0.3", -                "public-encrypt": "^4.0.0", -                "randombytes": "^2.0.0", -                "randomfill": "^1.0.3" -            }, -            "engines": { -                "node": "*" -            } -        },          "node_modules/css": {              "version": "3.0.0",              "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -1780,12 +3194,6 @@                  "node": ">=14"              }          }, -        "node_modules/dash-ast": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", -            "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", -            "dev": true -        },          "node_modules/data-urls": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", @@ -1878,6 +3286,34 @@                  "node": ">=0.10"              }          }, +        "node_modules/dedent": { +            "version": "1.5.1", +            "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", +            "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "peerDependencies": { +                "babel-plugin-macros": "^3.1.0" +            }, +            "peerDependenciesMeta": { +                "babel-plugin-macros": { +                    "optional": true +                } +            } +        }, +        "node_modules/deep-eql": { +            "version": "4.1.3", +            "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", +            "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", +            "dev": true, +            "dependencies": { +                "type-detect": "^4.0.0" +            }, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/deep-is": {              "version": "0.1.4",              "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1893,24 +3329,6 @@                  "node": ">=0.10.0"              }          }, -        "node_modules/define-properties": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", -            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", -            "dev": true, -            "dependencies": { -                "object-keys": "^1.0.12" -            }, -            "engines": { -                "node": ">= 0.4" -            } -        }, -        "node_modules/defined": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", -            "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", -            "dev": true -        },          "node_modules/delayed-stream": {              "version": "1.0.0",              "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1920,46 +3338,15 @@                  "node": ">=0.4.0"              }          }, -        "node_modules/deps-sort": { -            "version": "2.0.1", -            "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", -            "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", -            "dev": true, -            "dependencies": { -                "JSONStream": "^1.0.3", -                "shasum-object": "^1.0.0", -                "subarg": "^1.0.0", -                "through2": "^2.0.0" -            }, -            "bin": { -                "deps-sort": "bin/cmd.js" -            } -        }, -        "node_modules/des.js": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", -            "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.1", -                "minimalistic-assert": "^1.0.0" -            } -        }, -        "node_modules/detective": { -            "version": "5.2.0", -            "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", -            "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", +        "node_modules/detect-newline": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", +            "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",              "dev": true, -            "dependencies": { -                "acorn-node": "^1.6.1", -                "defined": "^1.0.0", -                "minimist": "^1.1.1" -            }, -            "bin": { -                "detective": "bin/detective.js" -            }, +            "optional": true, +            "peer": true,              "engines": { -                "node": ">=0.8.0" +                "node": ">=8"              }          },          "node_modules/dexie": { @@ -1980,23 +3367,15 @@                  "dexie": "^2.0.4 || ^3.0.0 || ^4.0.1-alpha.5"              }          }, -        "node_modules/diffie-hellman": { -            "version": "5.0.3", -            "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", -            "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", +        "node_modules/diff-sequences": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", +            "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",              "dev": true, -            "dependencies": { -                "bn.js": "^4.1.0", -                "miller-rabin": "^4.0.0", -                "randombytes": "^2.0.0" +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0"              }          }, -        "node_modules/diffie-hellman/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true -        },          "node_modules/dir-glob": {              "version": "3.0.1",              "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2021,16 +3400,6 @@                  "node": ">=6.0.0"              }          }, -        "node_modules/domain-browser": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", -            "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", -            "dev": true, -            "engines": { -                "node": ">=0.4", -                "npm": ">=1.2" -            } -        },          "node_modules/domexception": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -2043,41 +3412,33 @@                  "node": ">=12"              }          }, -        "node_modules/duplexer2": { -            "version": "0.1.4", -            "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", -            "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", -            "dev": true, -            "dependencies": { -                "readable-stream": "^2.0.2" -            } -        },          "node_modules/eastasianwidth": {              "version": "0.2.0",              "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",              "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",              "dev": true          }, -        "node_modules/elliptic": { -            "version": "6.5.4", -            "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", -            "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", +        "node_modules/electron-to-chromium": { +            "version": "1.4.576", +            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz", +            "integrity": "sha512-yXsZyXJfAqzWk1WKryr0Wl0MN2D47xodPvEEwlVePBnhU5E7raevLQR+E6b9JAD3GfL/7MbAL9ZtWQQPcLx7wA==",              "dev": true, -            "dependencies": { -                "bn.js": "^4.11.9", -                "brorand": "^1.1.0", -                "hash.js": "^1.0.0", -                "hmac-drbg": "^1.0.1", -                "inherits": "^2.0.4", -                "minimalistic-assert": "^1.0.1", -                "minimalistic-crypto-utils": "^1.0.1" -            } +            "optional": true, +            "peer": true          }, -        "node_modules/elliptic/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true +        "node_modules/emittery": { +            "version": "0.13.1", +            "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", +            "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=12" +            }, +            "funding": { +                "url": "https://github.com/sindresorhus/emittery?sponsor=1" +            }          },          "node_modules/emoji-regex": {              "version": "8.0.0", @@ -2106,57 +3467,6 @@                  "is-arrayish": "^0.2.1"              }          }, -        "node_modules/es-abstract": { -            "version": "1.19.2", -            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", -            "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", -            "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "es-to-primitive": "^1.2.1", -                "function-bind": "^1.1.1", -                "get-intrinsic": "^1.1.1", -                "get-symbol-description": "^1.0.0", -                "has": "^1.0.3", -                "has-symbols": "^1.0.3", -                "internal-slot": "^1.0.3", -                "is-callable": "^1.2.4", -                "is-negative-zero": "^2.0.2", -                "is-regex": "^1.1.4", -                "is-shared-array-buffer": "^1.0.1", -                "is-string": "^1.0.7", -                "is-weakref": "^1.0.2", -                "object-inspect": "^1.12.0", -                "object-keys": "^1.1.1", -                "object.assign": "^4.1.2", -                "string.prototype.trimend": "^1.0.4", -                "string.prototype.trimstart": "^1.0.4", -                "unbox-primitive": "^1.0.1" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/es-to-primitive": { -            "version": "1.2.1", -            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", -            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", -            "dev": true, -            "dependencies": { -                "is-callable": "^1.1.4", -                "is-date-object": "^1.0.1", -                "is-symbol": "^1.0.2" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/esbuild": {              "version": "0.19.5",              "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", @@ -2194,6 +3504,17 @@                  "@esbuild/win32-x64": "0.19.5"              }          }, +        "node_modules/escalade": { +            "version": "3.1.1", +            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", +            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/escape-string-regexp": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2335,6 +3656,129 @@                  "eslint": "^6 || ^7 || ^8"              }          }, +        "node_modules/eslint-plugin-vitest": { +            "version": "0.3.9", +            "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.9.tgz", +            "integrity": "sha512-ZGrz8dWFlotM5dwrsMLP4VcY5MizwKNV4JTnY0VKdnuCZ+qeEUMHf1qd8kRGQA3tqLvXcV929wt2ANkduq2Pgw==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/utils": "^6.9.1" +            }, +            "engines": { +                "node": "14.x || >= 16" +            }, +            "peerDependencies": { +                "eslint": ">=8.0.0", +                "vitest": "*" +            }, +            "peerDependenciesMeta": { +                "@typescript-eslint/eslint-plugin": { +                    "optional": true +                }, +                "vitest": { +                    "optional": true +                } +            } +        }, +        "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { +            "version": "6.10.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", +            "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.10.0", +                "@typescript-eslint/visitor-keys": "6.10.0" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { +            "version": "6.10.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", +            "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", +            "dev": true, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { +            "version": "6.10.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", +            "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.10.0", +                "@typescript-eslint/visitor-keys": "6.10.0", +                "debug": "^4.3.4", +                "globby": "^11.1.0", +                "is-glob": "^4.0.3", +                "semver": "^7.5.4", +                "ts-api-utils": "^1.0.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependenciesMeta": { +                "typescript": { +                    "optional": true +                } +            } +        }, +        "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { +            "version": "6.10.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", +            "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", +            "dev": true, +            "dependencies": { +                "@eslint-community/eslint-utils": "^4.4.0", +                "@types/json-schema": "^7.0.12", +                "@types/semver": "^7.5.0", +                "@typescript-eslint/scope-manager": "6.10.0", +                "@typescript-eslint/types": "6.10.0", +                "@typescript-eslint/typescript-estree": "6.10.0", +                "semver": "^7.5.4" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependencies": { +                "eslint": "^7.0.0 || ^8.0.0" +            } +        }, +        "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { +            "version": "6.10.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", +            "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.10.0", +                "eslint-visitor-keys": "^3.4.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        },          "node_modules/eslint-scope": {              "version": "7.2.2",              "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -2402,6 +3846,21 @@                  "url": "https://opencollective.com/eslint"              }          }, +        "node_modules/esprima": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", +            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "esparse": "bin/esparse.js", +                "esvalidate": "bin/esvalidate.js" +            }, +            "engines": { +                "node": ">=4" +            } +        },          "node_modules/esquery": {              "version": "1.5.0",              "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -2444,23 +3903,58 @@                  "node": ">=0.10.0"              }          }, -        "node_modules/events": { -            "version": "3.3.0", -            "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", -            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", +        "node_modules/execa": { +            "version": "5.1.1", +            "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", +            "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",              "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "cross-spawn": "^7.0.3", +                "get-stream": "^6.0.0", +                "human-signals": "^2.1.0", +                "is-stream": "^2.0.0", +                "merge-stream": "^2.0.0", +                "npm-run-path": "^4.0.1", +                "onetime": "^5.1.2", +                "signal-exit": "^3.0.3", +                "strip-final-newline": "^2.0.0" +            },              "engines": { -                "node": ">=0.8.x" +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/sindresorhus/execa?sponsor=1"              }          }, -        "node_modules/evp_bytestokey": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", -            "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", +        "node_modules/exit": { +            "version": "0.1.2", +            "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", +            "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",              "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">= 0.8.0" +            } +        }, +        "node_modules/expect": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", +            "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", +            "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "md5.js": "^1.3.4", -                "safe-buffer": "^5.1.1" +                "@jest/expect-utils": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0"              }          },          "node_modules/fake-indexeddb": { @@ -2518,12 +4012,6 @@              "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",              "dev": true          }, -        "node_modules/fast-safe-stringify": { -            "version": "2.1.1", -            "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", -            "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", -            "dev": true -        },          "node_modules/fastest-levenshtein": {              "version": "1.0.16",              "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -2542,6 +4030,17 @@                  "reusify": "^1.0.4"              }          }, +        "node_modules/fb-watchman": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", +            "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "bser": "2.1.1" +            } +        },          "node_modules/file-entry-cache": {              "version": "6.0.1",              "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2602,12 +4101,6 @@              "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",              "dev": true          }, -        "node_modules/foreach": { -            "version": "2.0.5", -            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", -            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", -            "dev": true -        },          "node_modules/foreground-child": {              "version": "3.1.1",              "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -2676,40 +4169,60 @@              "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",              "dev": true          }, -        "node_modules/get-assigned-identifiers": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", -            "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", -            "dev": true +        "node_modules/gensync": { +            "version": "1.0.0-beta.2", +            "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", +            "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6.9.0" +            }          }, -        "node_modules/get-intrinsic": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", -            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", +        "node_modules/get-caller-file": { +            "version": "2.0.5", +            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", +            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",              "dev": true, -            "dependencies": { -                "function-bind": "^1.1.1", -                "has": "^1.0.3", -                "has-symbols": "^1.0.1" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +            "optional": true, +            "peer": true, +            "engines": { +                "node": "6.* || 8.* || >= 10.*"              }          }, -        "node_modules/get-symbol-description": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", -            "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", +        "node_modules/get-func-name": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", +            "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",              "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "get-intrinsic": "^1.1.1" -            },              "engines": { -                "node": ">= 0.4" +                "node": "*" +            } +        }, +        "node_modules/get-package-type": { +            "version": "0.1.0", +            "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", +            "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8.0.0" +            } +        }, +        "node_modules/get-stream": { +            "version": "6.0.1", +            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", +            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=10"              },              "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "url": "https://github.com/sponsors/sindresorhus"              }          },          "node_modules/glob": { @@ -2823,6 +4336,14 @@              "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=",              "dev": true          }, +        "node_modules/graceful-fs": { +            "version": "4.2.11", +            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", +            "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "node_modules/graphemer": {              "version": "1.4.0",              "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2880,15 +4401,6 @@                  "node": ">= 0.4.0"              }          }, -        "node_modules/has-bigints": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", -            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", -            "dev": true, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/has-flag": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2898,82 +4410,6 @@                  "node": ">=8"              }          }, -        "node_modules/has-symbols": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", -            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", -            "dev": true, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/has-tostringtag": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", -            "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", -            "dev": true, -            "dependencies": { -                "has-symbols": "^1.0.2" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/hash-base": { -            "version": "3.1.0", -            "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", -            "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.4", -                "readable-stream": "^3.6.0", -                "safe-buffer": "^5.2.0" -            }, -            "engines": { -                "node": ">=4" -            } -        }, -        "node_modules/hash-base/node_modules/readable-stream": { -            "version": "3.6.0", -            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.3", -                "string_decoder": "^1.1.1", -                "util-deprecate": "^1.0.1" -            }, -            "engines": { -                "node": ">= 6" -            } -        }, -        "node_modules/hash.js": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", -            "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.3", -                "minimalistic-assert": "^1.0.1" -            } -        }, -        "node_modules/hmac-drbg": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", -            "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", -            "dev": true, -            "dependencies": { -                "hash.js": "^1.0.3", -                "minimalistic-assert": "^1.0.0", -                "minimalistic-crypto-utils": "^1.0.1" -            } -        },          "node_modules/hosted-git-info": {              "version": "4.1.0",              "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -2998,6 +4434,12 @@                  "node": ">=12"              }          }, +        "node_modules/html-escaper": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", +            "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", +            "dev": true +        },          "node_modules/html-tags": {              "version": "3.3.1",              "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", @@ -3101,15 +4543,6 @@                  "url": "https://github.com/sponsors/isaacs"              }          }, -        "node_modules/htmlescape": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", -            "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", -            "dev": true, -            "engines": { -                "node": ">=0.10" -            } -        },          "node_modules/http-proxy-agent": {              "version": "5.0.0",              "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -3124,12 +4557,6 @@                  "node": ">= 6"              }          }, -        "node_modules/https-browserify": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", -            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", -            "dev": true -        },          "node_modules/https-proxy-agent": {              "version": "5.0.1",              "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -3143,25 +4570,16 @@                  "node": ">= 6"              }          }, -        "node_modules/ieee754": { -            "version": "1.2.1", -            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", -            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", +        "node_modules/human-signals": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", +            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",              "dev": true, -            "funding": [ -                { -                    "type": "github", -                    "url": "https://github.com/sponsors/feross" -                }, -                { -                    "type": "patreon", -                    "url": "https://www.patreon.com/feross" -                }, -                { -                    "type": "consulting", -                    "url": "https://feross.org/support" -                } -            ] +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=10.17.0" +            }          },          "node_modules/ignore": {              "version": "5.2.4", @@ -3203,6 +4621,27 @@                  "node": ">=8"              }          }, +        "node_modules/import-local": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", +            "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "pkg-dir": "^4.2.0", +                "resolve-cwd": "^3.0.0" +            }, +            "bin": { +                "import-local-fixture": "fixtures/cli.js" +            }, +            "engines": { +                "node": ">=8" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        },          "node_modules/imurmurhash": {              "version": "0.1.4",              "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3246,106 +4685,12 @@              "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",              "dev": true          }, -        "node_modules/inline-source-map": { -            "version": "0.6.2", -            "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", -            "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", -            "dev": true, -            "dependencies": { -                "source-map": "~0.5.3" -            } -        }, -        "node_modules/insert-module-globals": { -            "version": "7.2.1", -            "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", -            "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", -            "dev": true, -            "dependencies": { -                "acorn-node": "^1.5.2", -                "combine-source-map": "^0.8.0", -                "concat-stream": "^1.6.1", -                "is-buffer": "^1.1.0", -                "JSONStream": "^1.0.3", -                "path-is-absolute": "^1.0.1", -                "process": "~0.11.0", -                "through2": "^2.0.0", -                "undeclared-identifiers": "^1.1.2", -                "xtend": "^4.0.0" -            }, -            "bin": { -                "insert-module-globals": "bin/cmd.js" -            } -        }, -        "node_modules/internal-slot": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", -            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", -            "dev": true, -            "dependencies": { -                "get-intrinsic": "^1.1.0", -                "has": "^1.0.3", -                "side-channel": "^1.0.4" -            }, -            "engines": { -                "node": ">= 0.4" -            } -        }, -        "node_modules/is-arguments": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", -            "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", -            "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/is-arrayish": {              "version": "0.2.1",              "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",              "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",              "dev": true          }, -        "node_modules/is-bigint": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", -            "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", -            "dev": true, -            "dependencies": { -                "has-bigints": "^1.0.1" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/is-boolean-object": { -            "version": "1.1.2", -            "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", -            "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", -            "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/is-buffer": { -            "version": "1.1.6", -            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", -            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", -            "dev": true -        },          "node_modules/is-builtin-module": {              "version": "3.2.1",              "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", @@ -3361,18 +4706,6 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/is-callable": { -            "version": "1.2.4", -            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", -            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", -            "dev": true, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/is-core-module": {              "version": "2.8.1",              "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -3385,21 +4718,6 @@                  "url": "https://github.com/sponsors/ljharb"              }          }, -        "node_modules/is-date-object": { -            "version": "1.0.5", -            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", -            "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", -            "dev": true, -            "dependencies": { -                "has-tostringtag": "^1.0.0" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/is-extglob": {              "version": "2.1.1",              "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3418,19 +4736,15 @@                  "node": ">=8"              }          }, -        "node_modules/is-generator-function": { -            "version": "1.0.10", -            "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", -            "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", +        "node_modules/is-generator-fn": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", +            "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",              "dev": true, -            "dependencies": { -                "has-tostringtag": "^1.0.0" -            }, +            "optional": true, +            "peer": true,              "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "node": ">=6"              }          },          "node_modules/is-glob": { @@ -3445,18 +4759,6 @@                  "node": ">=0.10.0"              }          }, -        "node_modules/is-negative-zero": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", -            "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", -            "dev": true, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/is-number": {              "version": "7.0.0",              "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3466,21 +4768,6 @@                  "node": ">=0.12.0"              }          }, -        "node_modules/is-number-object": { -            "version": "1.0.7", -            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", -            "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", -            "dev": true, -            "dependencies": { -                "has-tostringtag": "^1.0.0" -            }, -            "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/is-path-inside": {              "version": "3.0.3",              "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -3514,107 +4801,109 @@              "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",              "dev": true          }, -        "node_modules/is-regex": { -            "version": "1.1.4", -            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", -            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", +        "node_modules/is-stream": { +            "version": "2.0.1", +            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", +            "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",              "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" -            }, +            "optional": true, +            "peer": true,              "engines": { -                "node": ">= 0.4" +                "node": ">=8"              },              "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/is-shared-array-buffer": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", -            "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", +        "node_modules/isarray": { +            "version": "1.0.0", +            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", +            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", +            "dev": true +        }, +        "node_modules/isexe": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", +            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", +            "dev": true +        }, +        "node_modules/istanbul-lib-coverage": { +            "version": "3.2.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", +            "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==",              "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +            "engines": { +                "node": ">=8"              }          }, -        "node_modules/is-string": { -            "version": "1.0.7", -            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", -            "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", +        "node_modules/istanbul-lib-instrument": { +            "version": "6.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", +            "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "has-tostringtag": "^1.0.0" +                "@babel/core": "^7.12.3", +                "@babel/parser": "^7.14.7", +                "@istanbuljs/schema": "^0.1.2", +                "istanbul-lib-coverage": "^3.2.0", +                "semver": "^7.5.4"              },              "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "node": ">=10"              }          }, -        "node_modules/is-symbol": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", -            "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", +        "node_modules/istanbul-lib-report": { +            "version": "3.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", +            "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",              "dev": true,              "dependencies": { -                "has-symbols": "^1.0.2" +                "istanbul-lib-coverage": "^3.0.0", +                "make-dir": "^4.0.0", +                "supports-color": "^7.1.0"              },              "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "node": ">=10"              }          }, -        "node_modules/is-typed-array": { -            "version": "1.1.8", -            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", -            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", +        "node_modules/istanbul-lib-source-maps": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", +            "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",              "dev": true,              "dependencies": { -                "available-typed-arrays": "^1.0.5", -                "call-bind": "^1.0.2", -                "es-abstract": "^1.18.5", -                "foreach": "^2.0.5", -                "has-tostringtag": "^1.0.0" +                "debug": "^4.1.1", +                "istanbul-lib-coverage": "^3.0.0", +                "source-map": "^0.6.1"              },              "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "node": ">=10"              }          }, -        "node_modules/is-weakref": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", -            "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", +        "node_modules/istanbul-lib-source-maps/node_modules/source-map": { +            "version": "0.6.1", +            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", +            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", +            "dev": true, +            "engines": { +                "node": ">=0.10.0" +            } +        }, +        "node_modules/istanbul-reports": { +            "version": "3.1.6", +            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", +            "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",              "dev": true,              "dependencies": { -                "call-bind": "^1.0.2" +                "html-escaper": "^2.0.0", +                "istanbul-lib-report": "^3.0.0"              }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +            "engines": { +                "node": ">=8"              }          }, -        "node_modules/isarray": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", -            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", -            "dev": true -        }, -        "node_modules/isexe": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", -            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", -            "dev": true -        },          "node_modules/jackspeak": {              "version": "2.3.6",              "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -3633,6 +4922,603 @@                  "@pkgjs/parseargs": "^0.11.0"              }          }, +        "node_modules/jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", +            "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/core": "^29.7.0", +                "@jest/types": "^29.6.3", +                "import-local": "^3.0.2", +                "jest-cli": "^29.7.0" +            }, +            "bin": { +                "jest": "bin/jest.js" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" +            }, +            "peerDependenciesMeta": { +                "node-notifier": { +                    "optional": true +                } +            } +        }, +        "node_modules/jest-changed-files": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", +            "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "execa": "^5.0.0", +                "jest-util": "^29.7.0", +                "p-limit": "^3.1.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-circus": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", +            "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/environment": "^29.7.0", +                "@jest/expect": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "co": "^4.6.0", +                "dedent": "^1.0.0", +                "is-generator-fn": "^2.0.0", +                "jest-each": "^29.7.0", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "p-limit": "^3.1.0", +                "pretty-format": "^29.7.0", +                "pure-rand": "^6.0.0", +                "slash": "^3.0.0", +                "stack-utils": "^2.0.3" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-cli": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", +            "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/core": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "create-jest": "^29.7.0", +                "exit": "^0.1.2", +                "import-local": "^3.0.2", +                "jest-config": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "yargs": "^17.3.1" +            }, +            "bin": { +                "jest": "bin/jest.js" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" +            }, +            "peerDependenciesMeta": { +                "node-notifier": { +                    "optional": true +                } +            } +        }, +        "node_modules/jest-config": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", +            "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/core": "^7.11.6", +                "@jest/test-sequencer": "^29.7.0", +                "@jest/types": "^29.6.3", +                "babel-jest": "^29.7.0", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "deepmerge": "^4.2.2", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "jest-circus": "^29.7.0", +                "jest-environment-node": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-runner": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "micromatch": "^4.0.4", +                "parse-json": "^5.2.0", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "strip-json-comments": "^3.1.1" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "@types/node": "*", +                "ts-node": ">=9.0.0" +            }, +            "peerDependenciesMeta": { +                "@types/node": { +                    "optional": true +                }, +                "ts-node": { +                    "optional": true +                } +            } +        }, +        "node_modules/jest-diff": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", +            "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "chalk": "^4.0.0", +                "diff-sequences": "^29.6.3", +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-docblock": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", +            "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "detect-newline": "^3.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-each": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", +            "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "jest-get-type": "^29.6.3", +                "jest-util": "^29.7.0", +                "pretty-format": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-environment-node": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", +            "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/environment": "^29.7.0", +                "@jest/fake-timers": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-mock": "^29.7.0", +                "jest-util": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-get-type": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", +            "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-haste-map": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", +            "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "@types/graceful-fs": "^4.1.3", +                "@types/node": "*", +                "anymatch": "^3.0.3", +                "fb-watchman": "^2.0.0", +                "graceful-fs": "^4.2.9", +                "jest-regex-util": "^29.6.3", +                "jest-util": "^29.7.0", +                "jest-worker": "^29.7.0", +                "micromatch": "^4.0.4", +                "walker": "^1.0.8" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            }, +            "optionalDependencies": { +                "fsevents": "^2.3.2" +            } +        }, +        "node_modules/jest-leak-detector": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", +            "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-matcher-utils": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", +            "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "chalk": "^4.0.0", +                "jest-diff": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-message-util": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", +            "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/code-frame": "^7.12.13", +                "@jest/types": "^29.6.3", +                "@types/stack-utils": "^2.0.0", +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "micromatch": "^4.0.4", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "stack-utils": "^2.0.3" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-mock": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", +            "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-util": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-pnp-resolver": { +            "version": "1.2.3", +            "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", +            "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            }, +            "peerDependencies": { +                "jest-resolve": "*" +            }, +            "peerDependenciesMeta": { +                "jest-resolve": { +                    "optional": true +                } +            } +        }, +        "node_modules/jest-regex-util": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", +            "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-resolve": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", +            "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-pnp-resolver": "^1.2.2", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "resolve": "^1.20.0", +                "resolve.exports": "^2.0.0", +                "slash": "^3.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-resolve-dependencies": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", +            "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "jest-regex-util": "^29.6.3", +                "jest-snapshot": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-runner": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", +            "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/console": "^29.7.0", +                "@jest/environment": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "emittery": "^0.13.1", +                "graceful-fs": "^4.2.9", +                "jest-docblock": "^29.7.0", +                "jest-environment-node": "^29.7.0", +                "jest-haste-map": "^29.7.0", +                "jest-leak-detector": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-resolve": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-watcher": "^29.7.0", +                "jest-worker": "^29.7.0", +                "p-limit": "^3.1.0", +                "source-map-support": "0.5.13" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-runtime": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", +            "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/environment": "^29.7.0", +                "@jest/fake-timers": "^29.7.0", +                "@jest/globals": "^29.7.0", +                "@jest/source-map": "^29.6.3", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "cjs-module-lexer": "^1.0.0", +                "collect-v8-coverage": "^1.0.0", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-mock": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "slash": "^3.0.0", +                "strip-bom": "^4.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-snapshot": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", +            "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@babel/core": "^7.11.6", +                "@babel/generator": "^7.7.2", +                "@babel/plugin-syntax-jsx": "^7.7.2", +                "@babel/plugin-syntax-typescript": "^7.7.2", +                "@babel/types": "^7.3.3", +                "@jest/expect-utils": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "babel-preset-current-node-syntax": "^1.0.0", +                "chalk": "^4.0.0", +                "expect": "^29.7.0", +                "graceful-fs": "^4.2.9", +                "jest-diff": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "natural-compare": "^1.4.0", +                "pretty-format": "^29.7.0", +                "semver": "^7.5.3" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-util": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", +            "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "graceful-fs": "^4.2.9", +                "picomatch": "^2.2.3" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-validate": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", +            "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/types": "^29.6.3", +                "camelcase": "^6.2.0", +                "chalk": "^4.0.0", +                "jest-get-type": "^29.6.3", +                "leven": "^3.1.0", +                "pretty-format": "^29.7.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-watcher": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", +            "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "ansi-escapes": "^4.2.1", +                "chalk": "^4.0.0", +                "emittery": "^0.13.1", +                "jest-util": "^29.7.0", +                "string-length": "^4.0.1" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-worker": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", +            "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "@types/node": "*", +                "jest-util": "^29.7.0", +                "merge-stream": "^2.0.0", +                "supports-color": "^8.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/jest-worker/node_modules/supports-color": { +            "version": "8.1.1", +            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", +            "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "has-flag": "^4.0.0" +            }, +            "engines": { +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/chalk/supports-color?sponsor=1" +            } +        },          "node_modules/js-tokens": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3702,6 +5588,20 @@                  }              }          }, +        "node_modules/jsesc": { +            "version": "2.5.2", +            "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", +            "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "jsesc": "bin/jsesc" +            }, +            "engines": { +                "node": ">=4" +            } +        },          "node_modules/json-buffer": {              "version": "3.0.1",              "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3726,6 +5626,20 @@              "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",              "dev": true          }, +        "node_modules/json5": { +            "version": "2.2.3", +            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", +            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "bin": { +                "json5": "lib/cli.js" +            }, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/jsonc-eslint-parser": {              "version": "2.1.0",              "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz", @@ -3744,30 +5658,11 @@                  "url": "https://github.com/sponsors/ota-meshi"              }          }, -        "node_modules/jsonparse": { -            "version": "1.3.1", -            "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", -            "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", -            "dev": true, -            "engines": [ -                "node >= 0.2.0" -            ] -        }, -        "node_modules/JSONStream": { -            "version": "1.3.5", -            "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", -            "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", -            "dev": true, -            "dependencies": { -                "jsonparse": "^1.2.0", -                "through": ">=2.2.7 <3" -            }, -            "bin": { -                "JSONStream": "bin.js" -            }, -            "engines": { -                "node": "*" -            } +        "node_modules/jsonc-parser": { +            "version": "3.2.0", +            "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", +            "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", +            "dev": true          },          "node_modules/jszip": {              "version": "3.10.1", @@ -3814,14 +5709,15 @@              "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==",              "dev": true          }, -        "node_modules/labeled-stream-splicer": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", -            "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", +        "node_modules/leven": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", +            "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",              "dev": true, -            "dependencies": { -                "inherits": "^2.0.1", -                "stream-splicer": "^2.0.0" +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6"              }          },          "node_modules/levn": { @@ -3852,6 +5748,18 @@              "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",              "dev": true          }, +        "node_modules/local-pkg": { +            "version": "0.4.3", +            "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", +            "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", +            "dev": true, +            "engines": { +                "node": ">=14" +            }, +            "funding": { +                "url": "https://github.com/sponsors/antfu" +            } +        },          "node_modules/locate-path": {              "version": "6.0.0",              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3867,12 +5775,6 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/lodash.memoize": { -            "version": "3.0.4", -            "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", -            "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", -            "dev": true -        },          "node_modules/lodash.merge": {              "version": "4.6.2",              "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3885,6 +5787,15 @@              "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",              "dev": true          }, +        "node_modules/loupe": { +            "version": "2.3.7", +            "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", +            "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", +            "dev": true, +            "dependencies": { +                "get-func-name": "^2.0.1" +            } +        },          "node_modules/lru-cache": {              "version": "6.0.0",              "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3897,6 +5808,44 @@                  "node": ">=10"              }          }, +        "node_modules/magic-string": { +            "version": "0.30.5", +            "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", +            "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", +            "dev": true, +            "dependencies": { +                "@jridgewell/sourcemap-codec": "^1.4.15" +            }, +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/make-dir": { +            "version": "4.0.0", +            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", +            "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", +            "dev": true, +            "dependencies": { +                "semver": "^7.5.3" +            }, +            "engines": { +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/makeerror": { +            "version": "1.0.12", +            "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", +            "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "tmpl": "1.0.5" +            } +        },          "node_modules/map-obj": {              "version": "4.3.0",              "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -3919,17 +5868,6 @@                  "url": "https://github.com/sponsors/wooorm"              }          }, -        "node_modules/md5.js": { -            "version": "1.3.5", -            "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", -            "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", -            "dev": true, -            "dependencies": { -                "hash-base": "^3.0.0", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.1.2" -            } -        },          "node_modules/mdn-data": {              "version": "2.0.30",              "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -3974,6 +5912,14 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, +        "node_modules/merge-stream": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", +            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "node_modules/merge2": {              "version": "1.4.1",              "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3996,25 +5942,6 @@                  "node": ">=8.6"              }          }, -        "node_modules/miller-rabin": { -            "version": "4.0.1", -            "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", -            "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", -            "dev": true, -            "dependencies": { -                "bn.js": "^4.0.0", -                "brorand": "^1.0.1" -            }, -            "bin": { -                "miller-rabin": "bin/miller-rabin" -            } -        }, -        "node_modules/miller-rabin/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true -        },          "node_modules/mime-db": {              "version": "1.52.0",              "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4036,6 +5963,17 @@                  "node": ">= 0.6"              }          }, +        "node_modules/mimic-fn": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", +            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/min-indent": {              "version": "1.0.1",              "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -4045,18 +5983,6 @@                  "node": ">=4"              }          }, -        "node_modules/minimalistic-assert": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", -            "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", -            "dev": true -        }, -        "node_modules/minimalistic-crypto-utils": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", -            "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", -            "dev": true -        },          "node_modules/minimatch": {              "version": "3.1.2",              "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4098,39 +6024,16 @@                  "node": ">=16 || 14 >=14.17"              }          }, -        "node_modules/mkdirp-classic": { -            "version": "0.5.3", -            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", -            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", -            "dev": true -        }, -        "node_modules/module-deps": { -            "version": "6.2.3", -            "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", -            "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", +        "node_modules/mlly": { +            "version": "1.4.2", +            "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", +            "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==",              "dev": true,              "dependencies": { -                "browser-resolve": "^2.0.0", -                "cached-path-relative": "^1.0.2", -                "concat-stream": "~1.6.0", -                "defined": "^1.0.0", -                "detective": "^5.2.0", -                "duplexer2": "^0.1.2", -                "inherits": "^2.0.1", -                "JSONStream": "^1.0.3", -                "parents": "^1.0.0", -                "readable-stream": "^2.0.2", -                "resolve": "^1.4.0", -                "stream-combiner2": "^1.1.1", -                "subarg": "^1.0.0", -                "through2": "^2.0.0", -                "xtend": "^4.0.0" -            }, -            "bin": { -                "module-deps": "bin/cmd.js" -            }, -            "engines": { -                "node": ">= 0.8.0" +                "acorn": "^8.10.0", +                "pathe": "^1.1.1", +                "pkg-types": "^1.0.3", +                "ufo": "^1.3.0"              }          },          "node_modules/ms": { @@ -4169,6 +6072,22 @@              "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",              "dev": true          }, +        "node_modules/node-int64": { +            "version": "0.4.0", +            "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", +            "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/node-releases": { +            "version": "2.0.13", +            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", +            "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "node_modules/normalize-package-data": {              "version": "3.0.3",              "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -4193,64 +6112,50 @@                  "node": ">=0.10.0"              }          }, +        "node_modules/npm-run-path": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", +            "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "path-key": "^3.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        },          "node_modules/nwsapi": {              "version": "2.2.7",              "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",              "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",              "dev": true          }, -        "node_modules/object-assign": { -            "version": "4.1.1", -            "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", -            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", -            "dev": true, -            "engines": { -                "node": ">=0.10.0" -            } -        }, -        "node_modules/object-inspect": { -            "version": "1.12.0", -            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", -            "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", -            "dev": true, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/object-keys": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", -            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", +        "node_modules/once": { +            "version": "1.4.0", +            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", +            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",              "dev": true, -            "engines": { -                "node": ">= 0.4" +            "dependencies": { +                "wrappy": "1"              }          }, -        "node_modules/object.assign": { -            "version": "4.1.2", -            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", -            "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", +        "node_modules/onetime": { +            "version": "5.1.2", +            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", +            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "call-bind": "^1.0.0", -                "define-properties": "^1.1.3", -                "has-symbols": "^1.0.1", -                "object-keys": "^1.1.1" +                "mimic-fn": "^2.1.0"              },              "engines": { -                "node": ">= 0.4" +                "node": ">=6"              },              "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/once": { -            "version": "1.4.0", -            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", -            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", -            "dev": true, -            "dependencies": { -                "wrappy": "1" +                "url": "https://github.com/sponsors/sindresorhus"              }          },          "node_modules/optionator": { @@ -4270,12 +6175,6 @@                  "node": ">= 0.8.0"              }          }, -        "node_modules/os-browserify": { -            "version": "0.3.0", -            "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", -            "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", -            "dev": true -        },          "node_modules/p-limit": {              "version": "3.1.0",              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4306,6 +6205,17 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, +        "node_modules/p-try": { +            "version": "2.2.0", +            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", +            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/pako": {              "version": "1.0.11",              "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -4324,28 +6234,6 @@                  "node": ">=6"              }          }, -        "node_modules/parents": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", -            "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", -            "dev": true, -            "dependencies": { -                "path-platform": "~0.11.15" -            } -        }, -        "node_modules/parse-asn1": { -            "version": "5.1.6", -            "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", -            "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", -            "dev": true, -            "dependencies": { -                "asn1.js": "^5.2.0", -                "browserify-aes": "^1.0.0", -                "evp_bytestokey": "^1.0.0", -                "pbkdf2": "^3.0.3", -                "safe-buffer": "^5.1.1" -            } -        },          "node_modules/parse-json": {              "version": "5.2.0",              "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4376,12 +6264,6 @@                  "url": "https://github.com/inikulin/parse5?sponsor=1"              }          }, -        "node_modules/path-browserify": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", -            "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", -            "dev": true -        },          "node_modules/path-exists": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4413,16 +6295,9 @@              "version": "1.0.7",              "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",              "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", -            "dev": true -        }, -        "node_modules/path-platform": { -            "version": "0.11.15", -            "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", -            "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",              "dev": true, -            "engines": { -                "node": ">= 0.8.0" -            } +            "optional": true, +            "peer": true          },          "node_modules/path-scurry": {              "version": "1.10.1", @@ -4458,20 +6333,19 @@                  "node": ">=8"              }          }, -        "node_modules/pbkdf2": { -            "version": "3.1.2", -            "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", -            "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", +        "node_modules/pathe": { +            "version": "1.1.1", +            "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", +            "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", +            "dev": true +        }, +        "node_modules/pathval": { +            "version": "1.1.1", +            "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", +            "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",              "dev": true, -            "dependencies": { -                "create-hash": "^1.1.2", -                "create-hmac": "^1.1.4", -                "ripemd160": "^2.0.1", -                "safe-buffer": "^5.0.1", -                "sha.js": "^2.4.8" -            },              "engines": { -                "node": ">=0.12" +                "node": "*"              }          },          "node_modules/picocolors": { @@ -4492,6 +6366,102 @@                  "url": "https://github.com/sponsors/jonschlinkert"              }          }, +        "node_modules/pirates": { +            "version": "4.0.6", +            "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", +            "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">= 6" +            } +        }, +        "node_modules/pkg-dir": { +            "version": "4.2.0", +            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", +            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "find-up": "^4.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/pkg-dir/node_modules/find-up": { +            "version": "4.1.0", +            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", +            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "locate-path": "^5.0.0", +                "path-exists": "^4.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/pkg-dir/node_modules/locate-path": { +            "version": "5.0.0", +            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", +            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-locate": "^4.1.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/pkg-dir/node_modules/p-limit": { +            "version": "2.3.0", +            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", +            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-try": "^2.0.0" +            }, +            "engines": { +                "node": ">=6" +            }, +            "funding": { +                "url": "https://github.com/sponsors/sindresorhus" +            } +        }, +        "node_modules/pkg-dir/node_modules/p-locate": { +            "version": "4.1.0", +            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", +            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "p-limit": "^2.2.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/pkg-types": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", +            "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", +            "dev": true, +            "dependencies": { +                "jsonc-parser": "^3.2.0", +                "mlly": "^1.2.0", +                "pathe": "^1.1.0" +            } +        },          "node_modules/playwright": {              "version": "1.39.0",              "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", @@ -4600,13 +6570,30 @@                  "node": ">= 0.8.0"              }          }, -        "node_modules/process": { -            "version": "0.11.10", -            "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", -            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", +        "node_modules/pretty-format": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", +            "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", +            "dev": true, +            "dependencies": { +                "@jest/schemas": "^29.6.3", +                "ansi-styles": "^5.0.0", +                "react-is": "^18.0.0" +            }, +            "engines": { +                "node": "^14.15.0 || ^16.10.0 || >=18.0.0" +            } +        }, +        "node_modules/pretty-format/node_modules/ansi-styles": { +            "version": "5.2.0", +            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", +            "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",              "dev": true,              "engines": { -                "node": ">= 0.6.0" +                "node": ">=10" +            }, +            "funding": { +                "url": "https://github.com/chalk/ansi-styles?sponsor=1"              }          },          "node_modules/process-nextick-args": { @@ -4643,50 +6630,23 @@              "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",              "dev": true          }, -        "node_modules/public-encrypt": { -            "version": "4.0.3", -            "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", -            "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", +        "node_modules/pure-rand": { +            "version": "6.0.4", +            "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", +            "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==",              "dev": true, -            "dependencies": { -                "bn.js": "^4.1.0", -                "browserify-rsa": "^4.0.0", -                "create-hash": "^1.1.0", -                "parse-asn1": "^5.0.0", -                "randombytes": "^2.0.1", -                "safe-buffer": "^5.1.2" -            } -        }, -        "node_modules/public-encrypt/node_modules/bn.js": { -            "version": "4.12.0", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -            "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -            "dev": true -        }, -        "node_modules/punycode": { -            "version": "1.4.1", -            "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", -            "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", -            "dev": true -        }, -        "node_modules/querystring": { -            "version": "0.2.0", -            "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", -            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", -            "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", -            "dev": true, -            "engines": { -                "node": ">=0.4.x" -            } -        }, -        "node_modules/querystring-es3": { -            "version": "0.2.1", -            "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", -            "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", -            "dev": true, -            "engines": { -                "node": ">=0.4.x" -            } +            "funding": [ +                { +                    "type": "individual", +                    "url": "https://github.com/sponsors/dubzzz" +                }, +                { +                    "type": "opencollective", +                    "url": "https://opencollective.com/fast-check" +                } +            ], +            "optional": true, +            "peer": true          },          "node_modules/querystringify": {              "version": "2.2.0", @@ -4726,33 +6686,11 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/randombytes": { -            "version": "2.1.0", -            "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", -            "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", -            "dev": true, -            "dependencies": { -                "safe-buffer": "^5.1.0" -            } -        }, -        "node_modules/randomfill": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", -            "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", -            "dev": true, -            "dependencies": { -                "randombytes": "^2.0.5", -                "safe-buffer": "^5.1.0" -            } -        }, -        "node_modules/read-only-stream": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", -            "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", -            "dev": true, -            "dependencies": { -                "readable-stream": "^2.0.2" -            } +        "node_modules/react-is": { +            "version": "18.2.0", +            "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", +            "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", +            "dev": true          },          "node_modules/read-pkg": {              "version": "6.0.0", @@ -4859,6 +6797,17 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, +        "node_modules/require-directory": { +            "version": "2.1.1", +            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", +            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=0.10.0" +            } +        },          "node_modules/require-from-string": {              "version": "2.0.2",              "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4879,6 +6828,8 @@              "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",              "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": {                  "is-core-module": "^2.8.1",                  "path-parse": "^1.0.7", @@ -4891,6 +6842,31 @@                  "url": "https://github.com/sponsors/ljharb"              }          }, +        "node_modules/resolve-cwd": { +            "version": "3.0.0", +            "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", +            "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "resolve-from": "^5.0.0" +            }, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/resolve-cwd/node_modules/resolve-from": { +            "version": "5.0.0", +            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", +            "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8" +            } +        },          "node_modules/resolve-from": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4900,6 +6876,17 @@                  "node": ">=4"              }          }, +        "node_modules/resolve.exports": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", +            "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=10" +            } +        },          "node_modules/reusify": {              "version": "1.0.4",              "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4925,14 +6912,20 @@                  "url": "https://github.com/sponsors/isaacs"              }          }, -        "node_modules/ripemd160": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", -            "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", +        "node_modules/rollup": { +            "version": "3.29.4", +            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", +            "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",              "dev": true, -            "dependencies": { -                "hash-base": "^3.0.0", -                "inherits": "^2.0.1" +            "bin": { +                "rollup": "dist/bin/rollup" +            }, +            "engines": { +                "node": ">=14.18.0", +                "npm": ">=8.0.0" +            }, +            "optionalDependencies": { +                "fsevents": "~2.3.2"              }          },          "node_modules/rrweb-cssom": { @@ -4964,26 +6957,6 @@                  "queue-microtask": "^1.2.2"              }          }, -        "node_modules/safe-buffer": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", -            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", -            "dev": true, -            "funding": [ -                { -                    "type": "github", -                    "url": "https://github.com/sponsors/feross" -                }, -                { -                    "type": "patreon", -                    "url": "https://www.patreon.com/feross" -                }, -                { -                    "type": "consulting", -                    "url": "https://feross.org/support" -                } -            ] -        },          "node_modules/safer-buffer": {              "version": "2.1.2",              "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5023,28 +6996,6 @@              "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",              "dev": true          }, -        "node_modules/sha.js": { -            "version": "2.4.11", -            "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", -            "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" -            }, -            "bin": { -                "sha.js": "bin.js" -            } -        }, -        "node_modules/shasum-object": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", -            "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", -            "dev": true, -            "dependencies": { -                "fast-safe-stringify": "^2.0.7" -            } -        },          "node_modules/shebang-command": {              "version": "2.0.0",              "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5066,45 +7017,19 @@                  "node": ">=8"              }          }, -        "node_modules/shell-quote": { -            "version": "1.7.3", -            "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", -            "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", +        "node_modules/siginfo": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", +            "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",              "dev": true          }, -        "node_modules/side-channel": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", -            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", +        "node_modules/signal-exit": { +            "version": "3.0.7", +            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", +            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",              "dev": true, -            "dependencies": { -                "call-bind": "^1.0.0", -                "get-intrinsic": "^1.0.2", -                "object-inspect": "^1.9.0" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/simple-concat": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", -            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", -            "dev": true, -            "funding": [ -                { -                    "type": "github", -                    "url": "https://github.com/sponsors/feross" -                }, -                { -                    "type": "patreon", -                    "url": "https://www.patreon.com/feross" -                }, -                { -                    "type": "consulting", -                    "url": "https://feross.org/support" -                } -            ] +            "optional": true, +            "peer": true          },          "node_modules/sisteransi": {              "version": "1.0.5", @@ -5138,15 +7063,6 @@                  "url": "https://github.com/chalk/slice-ansi?sponsor=1"              }          }, -        "node_modules/source-map": { -            "version": "0.5.7", -            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", -            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", -            "dev": true, -            "engines": { -                "node": ">=0.10.0" -            } -        },          "node_modules/source-map-js": {              "version": "1.0.2",              "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5167,6 +7083,29 @@                  "decode-uri-component": "^0.2.0"              }          }, +        "node_modules/source-map-support": { +            "version": "0.5.13", +            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", +            "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "buffer-from": "^1.0.0", +                "source-map": "^0.6.0" +            } +        }, +        "node_modules/source-map-support/node_modules/source-map": { +            "version": "0.6.1", +            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", +            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=0.10.0" +            } +        },          "node_modules/spdx-correct": {              "version": "3.2.0",              "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -5199,83 +7138,64 @@              "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==",              "dev": true          }, -        "node_modules/stream-browserify": { -            "version": "3.0.0", -            "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", -            "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", +        "node_modules/sprintf-js": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", +            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",              "dev": true, -            "dependencies": { -                "inherits": "~2.0.4", -                "readable-stream": "^3.5.0" -            } +            "optional": true, +            "peer": true          }, -        "node_modules/stream-browserify/node_modules/readable-stream": { -            "version": "3.6.0", -            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", +        "node_modules/stack-utils": { +            "version": "2.0.6", +            "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", +            "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "inherits": "^2.0.3", -                "string_decoder": "^1.1.1", -                "util-deprecate": "^1.0.1" +                "escape-string-regexp": "^2.0.0"              },              "engines": { -                "node": ">= 6" +                "node": ">=10"              }          }, -        "node_modules/stream-combiner2": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", -            "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", +        "node_modules/stack-utils/node_modules/escape-string-regexp": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", +            "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",              "dev": true, -            "dependencies": { -                "duplexer2": "~0.1.0", -                "readable-stream": "^2.0.2" +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8"              }          }, -        "node_modules/stream-http": { -            "version": "3.2.0", -            "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", -            "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", -            "dev": true, -            "dependencies": { -                "builtin-status-codes": "^3.0.0", -                "inherits": "^2.0.4", -                "readable-stream": "^3.6.0", -                "xtend": "^4.0.2" -            } +        "node_modules/stackback": { +            "version": "0.0.2", +            "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", +            "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", +            "dev": true +        }, +        "node_modules/std-env": { +            "version": "3.4.3", +            "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", +            "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", +            "dev": true          }, -        "node_modules/stream-http/node_modules/readable-stream": { -            "version": "3.6.0", -            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", +        "node_modules/string-length": { +            "version": "4.0.2", +            "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", +            "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",              "dev": true, +            "optional": true, +            "peer": true,              "dependencies": { -                "inherits": "^2.0.3", -                "string_decoder": "^1.1.1", -                "util-deprecate": "^1.0.1" +                "char-regex": "^1.0.2", +                "strip-ansi": "^6.0.0"              },              "engines": { -                "node": ">= 6" -            } -        }, -        "node_modules/stream-splicer": { -            "version": "2.0.1", -            "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", -            "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", -            "dev": true, -            "dependencies": { -                "inherits": "^2.0.1", -                "readable-stream": "^2.0.2" -            } -        }, -        "node_modules/string_decoder": { -            "version": "1.3.0", -            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", -            "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", -            "dev": true, -            "dependencies": { -                "safe-buffer": "~5.2.0" +                "node": ">=10"              }          },          "node_modules/string-width": { @@ -5307,32 +7227,6 @@                  "node": ">=8"              }          }, -        "node_modules/string.prototype.trimend": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", -            "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", -            "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "define-properties": "^1.1.3" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/string.prototype.trimstart": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", -            "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", -            "dev": true, -            "dependencies": { -                "call-bind": "^1.0.2", -                "define-properties": "^1.1.3" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        },          "node_modules/strip-ansi": {              "version": "6.0.1",              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5358,6 +7252,28 @@                  "node": ">=8"              }          }, +        "node_modules/strip-bom": { +            "version": "4.0.0", +            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", +            "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=8" +            } +        }, +        "node_modules/strip-final-newline": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", +            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=6" +            } +        },          "node_modules/strip-indent": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", @@ -5385,6 +7301,18 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, +        "node_modules/strip-literal": { +            "version": "1.3.0", +            "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", +            "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", +            "dev": true, +            "dependencies": { +                "acorn": "^8.10.0" +            }, +            "funding": { +                "url": "https://github.com/sponsors/antfu" +            } +        },          "node_modules/style-search": {              "version": "0.1.0",              "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -5488,15 +7416,6 @@                  "node": ">=8"              }          }, -        "node_modules/subarg": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", -            "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", -            "dev": true, -            "dependencies": { -                "minimist": "^1.1.0" -            } -        },          "node_modules/supports-color": {              "version": "7.2.0",              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5527,6 +7446,8 @@              "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",              "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",              "dev": true, +            "optional": true, +            "peer": true,              "engines": {                  "node": ">= 0.4"              }, @@ -5546,15 +7467,6 @@              "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",              "dev": true          }, -        "node_modules/syntax-error": { -            "version": "1.4.0", -            "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", -            "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", -            "dev": true, -            "dependencies": { -                "acorn-node": "^1.2.0" -            } -        },          "node_modules/table": {              "version": "6.8.1",              "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", @@ -5571,38 +7483,67 @@                  "node": ">=10.0.0"              }          }, +        "node_modules/test-exclude": { +            "version": "6.0.0", +            "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", +            "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", +            "dev": true, +            "dependencies": { +                "@istanbuljs/schema": "^0.1.2", +                "glob": "^7.1.4", +                "minimatch": "^3.0.4" +            }, +            "engines": { +                "node": ">=8" +            } +        },          "node_modules/text-table": {              "version": "0.2.0",              "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",              "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",              "dev": true          }, -        "node_modules/through": { -            "version": "2.3.8", -            "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", -            "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", +        "node_modules/tinybench": { +            "version": "2.5.1", +            "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", +            "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==",              "dev": true          }, -        "node_modules/through2": { -            "version": "2.0.5", -            "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", -            "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", +        "node_modules/tinypool": { +            "version": "0.7.0", +            "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", +            "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==",              "dev": true, -            "dependencies": { -                "readable-stream": "~2.3.6", -                "xtend": "~4.0.1" +            "engines": { +                "node": ">=14.0.0"              }          }, -        "node_modules/timers-browserify": { -            "version": "1.4.2", -            "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", -            "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", +        "node_modules/tinyspy": { +            "version": "2.2.0", +            "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", +            "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==",              "dev": true, -            "dependencies": { -                "process": "~0.11.0" -            },              "engines": { -                "node": ">=0.6.0" +                "node": ">=14.0.0" +            } +        }, +        "node_modules/tmpl": { +            "version": "1.0.5", +            "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", +            "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node_modules/to-fast-properties": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", +            "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=4"              }          },          "node_modules/to-regex-range": { @@ -5674,11 +7615,17 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/tty-browserify": { -            "version": "0.0.1", -            "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", -            "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", -            "dev": true +        "node_modules/ts-api-utils": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", +            "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", +            "dev": true, +            "engines": { +                "node": ">=16.13.0" +            }, +            "peerDependencies": { +                "typescript": ">=4.2.0" +            }          },          "node_modules/type-check": {              "version": "0.4.0", @@ -5692,6 +7639,15 @@                  "node": ">= 0.8.0"              }          }, +        "node_modules/type-detect": { +            "version": "4.0.8", +            "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", +            "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", +            "dev": true, +            "engines": { +                "node": ">=4" +            } +        },          "node_modules/type-fest": {              "version": "0.20.2",              "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -5704,10 +7660,24 @@                  "url": "https://github.com/sponsors/sindresorhus"              }          }, -        "node_modules/typedarray": { -            "version": "0.0.6", -            "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", -            "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", +        "node_modules/typescript": { +            "version": "5.2.2", +            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", +            "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", +            "dev": true, +            "peer": true, +            "bin": { +                "tsc": "bin/tsc", +                "tsserver": "bin/tsserver" +            }, +            "engines": { +                "node": ">=14.17" +            } +        }, +        "node_modules/ufo": { +            "version": "1.3.1", +            "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.1.tgz", +            "integrity": "sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==",              "dev": true          },          "node_modules/uglify-js": { @@ -5723,46 +7693,6 @@                  "node": ">=0.8.0"              }          }, -        "node_modules/umd": { -            "version": "3.0.3", -            "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", -            "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", -            "dev": true, -            "bin": { -                "umd": "bin/cli.js" -            } -        }, -        "node_modules/unbox-primitive": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", -            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", -            "dev": true, -            "dependencies": { -                "function-bind": "^1.1.1", -                "has-bigints": "^1.0.1", -                "has-symbols": "^1.0.2", -                "which-boxed-primitive": "^1.0.2" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/undeclared-identifiers": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", -            "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", -            "dev": true, -            "dependencies": { -                "acorn-node": "^1.3.0", -                "dash-ast": "^1.0.0", -                "get-assigned-identifiers": "^1.2.0", -                "simple-concat": "^1.0.0", -                "xtend": "^4.0.1" -            }, -            "bin": { -                "undeclared-identifiers": "bin.js" -            } -        },          "node_modules/undici-types": {              "version": "5.26.5",              "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -5778,6 +7708,38 @@                  "node": ">= 4.0.0"              }          }, +        "node_modules/update-browserslist-db": { +            "version": "1.0.13", +            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", +            "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", +            "dev": true, +            "funding": [ +                { +                    "type": "opencollective", +                    "url": "https://opencollective.com/browserslist" +                }, +                { +                    "type": "tidelift", +                    "url": "https://tidelift.com/funding/github/npm/browserslist" +                }, +                { +                    "type": "github", +                    "url": "https://github.com/sponsors/ai" +                } +            ], +            "optional": true, +            "peer": true, +            "dependencies": { +                "escalade": "^3.1.1", +                "picocolors": "^1.0.0" +            }, +            "bin": { +                "update-browserslist-db": "cli.js" +            }, +            "peerDependencies": { +                "browserslist": ">= 4.21.0" +            } +        },          "node_modules/uri-js": {              "version": "4.4.1",              "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5796,16 +7758,6 @@                  "node": ">=6"              }          }, -        "node_modules/url": { -            "version": "0.11.0", -            "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", -            "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", -            "dev": true, -            "dependencies": { -                "punycode": "1.3.2", -                "querystring": "0.2.0" -            } -        },          "node_modules/url-parse": {              "version": "1.5.10",              "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -5816,30 +7768,30 @@                  "requires-port": "^1.0.0"              }          }, -        "node_modules/url/node_modules/punycode": { -            "version": "1.3.2", -            "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", -            "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", +        "node_modules/util-deprecate": { +            "version": "1.0.2", +            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", +            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",              "dev": true          }, -        "node_modules/util": { -            "version": "0.12.4", -            "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", -            "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", +        "node_modules/v8-to-istanbul": { +            "version": "9.1.3", +            "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", +            "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==",              "dev": true,              "dependencies": { -                "inherits": "^2.0.3", -                "is-arguments": "^1.0.4", -                "is-generator-function": "^1.0.7", -                "is-typed-array": "^1.1.3", -                "safe-buffer": "^5.1.2", -                "which-typed-array": "^1.1.2" +                "@jridgewell/trace-mapping": "^0.3.12", +                "@types/istanbul-lib-coverage": "^2.0.1", +                "convert-source-map": "^2.0.0" +            }, +            "engines": { +                "node": ">=10.12.0"              }          }, -        "node_modules/util-deprecate": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", -            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", +        "node_modules/v8-to-istanbul/node_modules/convert-source-map": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +            "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",              "dev": true          },          "node_modules/validate-npm-package-license": { @@ -5852,11 +7804,549 @@                  "spdx-expression-parse": "^3.0.0"              }          }, -        "node_modules/vm-browserify": { -            "version": "1.1.2", -            "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", -            "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", -            "dev": true +        "node_modules/vite": { +            "version": "4.5.0", +            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", +            "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", +            "dev": true, +            "dependencies": { +                "esbuild": "^0.18.10", +                "postcss": "^8.4.27", +                "rollup": "^3.27.1" +            }, +            "bin": { +                "vite": "bin/vite.js" +            }, +            "engines": { +                "node": "^14.18.0 || >=16.0.0" +            }, +            "funding": { +                "url": "https://github.com/vitejs/vite?sponsor=1" +            }, +            "optionalDependencies": { +                "fsevents": "~2.3.2" +            }, +            "peerDependencies": { +                "@types/node": ">= 14", +                "less": "*", +                "lightningcss": "^1.21.0", +                "sass": "*", +                "stylus": "*", +                "sugarss": "*", +                "terser": "^5.4.0" +            }, +            "peerDependenciesMeta": { +                "@types/node": { +                    "optional": true +                }, +                "less": { +                    "optional": true +                }, +                "lightningcss": { +                    "optional": true +                }, +                "sass": { +                    "optional": true +                }, +                "stylus": { +                    "optional": true +                }, +                "sugarss": { +                    "optional": true +                }, +                "terser": { +                    "optional": true +                } +            } +        }, +        "node_modules/vite-node": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", +            "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", +            "dev": true, +            "dependencies": { +                "cac": "^6.7.14", +                "debug": "^4.3.4", +                "mlly": "^1.4.0", +                "pathe": "^1.1.1", +                "picocolors": "^1.0.0", +                "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" +            }, +            "bin": { +                "vite-node": "vite-node.mjs" +            }, +            "engines": { +                "node": ">=v14.18.0" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/android-arm": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", +            "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", +            "cpu": [ +                "arm" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "android" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/android-arm64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", +            "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", +            "cpu": [ +                "arm64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "android" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/android-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", +            "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "android" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/darwin-arm64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", +            "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", +            "cpu": [ +                "arm64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "darwin" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/darwin-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", +            "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "darwin" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", +            "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", +            "cpu": [ +                "arm64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "freebsd" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/freebsd-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", +            "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "freebsd" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-arm": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", +            "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", +            "cpu": [ +                "arm" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-arm64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", +            "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", +            "cpu": [ +                "arm64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-ia32": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", +            "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", +            "cpu": [ +                "ia32" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-loong64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", +            "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", +            "cpu": [ +                "loong64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-mips64el": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", +            "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", +            "cpu": [ +                "mips64el" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-ppc64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", +            "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", +            "cpu": [ +                "ppc64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-riscv64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", +            "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", +            "cpu": [ +                "riscv64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-s390x": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", +            "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", +            "cpu": [ +                "s390x" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/linux-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", +            "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "linux" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/netbsd-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", +            "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "netbsd" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/openbsd-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", +            "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "openbsd" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/sunos-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", +            "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "sunos" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/win32-arm64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", +            "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", +            "cpu": [ +                "arm64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "win32" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/win32-ia32": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", +            "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", +            "cpu": [ +                "ia32" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "win32" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/@esbuild/win32-x64": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", +            "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", +            "cpu": [ +                "x64" +            ], +            "dev": true, +            "optional": true, +            "os": [ +                "win32" +            ], +            "engines": { +                "node": ">=12" +            } +        }, +        "node_modules/vite/node_modules/esbuild": { +            "version": "0.18.20", +            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", +            "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", +            "dev": true, +            "hasInstallScript": true, +            "bin": { +                "esbuild": "bin/esbuild" +            }, +            "engines": { +                "node": ">=12" +            }, +            "optionalDependencies": { +                "@esbuild/android-arm": "0.18.20", +                "@esbuild/android-arm64": "0.18.20", +                "@esbuild/android-x64": "0.18.20", +                "@esbuild/darwin-arm64": "0.18.20", +                "@esbuild/darwin-x64": "0.18.20", +                "@esbuild/freebsd-arm64": "0.18.20", +                "@esbuild/freebsd-x64": "0.18.20", +                "@esbuild/linux-arm": "0.18.20", +                "@esbuild/linux-arm64": "0.18.20", +                "@esbuild/linux-ia32": "0.18.20", +                "@esbuild/linux-loong64": "0.18.20", +                "@esbuild/linux-mips64el": "0.18.20", +                "@esbuild/linux-ppc64": "0.18.20", +                "@esbuild/linux-riscv64": "0.18.20", +                "@esbuild/linux-s390x": "0.18.20", +                "@esbuild/linux-x64": "0.18.20", +                "@esbuild/netbsd-x64": "0.18.20", +                "@esbuild/openbsd-x64": "0.18.20", +                "@esbuild/sunos-x64": "0.18.20", +                "@esbuild/win32-arm64": "0.18.20", +                "@esbuild/win32-ia32": "0.18.20", +                "@esbuild/win32-x64": "0.18.20" +            } +        }, +        "node_modules/vitest": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", +            "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", +            "dev": true, +            "dependencies": { +                "@types/chai": "^4.3.5", +                "@types/chai-subset": "^1.3.3", +                "@types/node": "*", +                "@vitest/expect": "0.34.6", +                "@vitest/runner": "0.34.6", +                "@vitest/snapshot": "0.34.6", +                "@vitest/spy": "0.34.6", +                "@vitest/utils": "0.34.6", +                "acorn": "^8.9.0", +                "acorn-walk": "^8.2.0", +                "cac": "^6.7.14", +                "chai": "^4.3.10", +                "debug": "^4.3.4", +                "local-pkg": "^0.4.3", +                "magic-string": "^0.30.1", +                "pathe": "^1.1.1", +                "picocolors": "^1.0.0", +                "std-env": "^3.3.3", +                "strip-literal": "^1.0.1", +                "tinybench": "^2.5.0", +                "tinypool": "^0.7.0", +                "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", +                "vite-node": "0.34.6", +                "why-is-node-running": "^2.2.2" +            }, +            "bin": { +                "vitest": "vitest.mjs" +            }, +            "engines": { +                "node": ">=v14.18.0" +            }, +            "funding": { +                "url": "https://opencollective.com/vitest" +            }, +            "peerDependencies": { +                "@edge-runtime/vm": "*", +                "@vitest/browser": "*", +                "@vitest/ui": "*", +                "happy-dom": "*", +                "jsdom": "*", +                "playwright": "*", +                "safaridriver": "*", +                "webdriverio": "*" +            }, +            "peerDependenciesMeta": { +                "@edge-runtime/vm": { +                    "optional": true +                }, +                "@vitest/browser": { +                    "optional": true +                }, +                "@vitest/ui": { +                    "optional": true +                }, +                "happy-dom": { +                    "optional": true +                }, +                "jsdom": { +                    "optional": true +                }, +                "playwright": { +                    "optional": true +                }, +                "safaridriver": { +                    "optional": true +                }, +                "webdriverio": { +                    "optional": true +                } +            }          },          "node_modules/w3c-xmlserializer": {              "version": "4.0.0", @@ -5870,6 +8360,17 @@                  "node": ">=14"              }          }, +        "node_modules/walker": { +            "version": "1.0.8", +            "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", +            "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "makeerror": "1.0.12" +            } +        },          "node_modules/wanakana": {              "version": "5.2.0",              "resolved": "https://registry.npmjs.org/wanakana/-/wanakana-5.2.0.tgz", @@ -5949,40 +8450,20 @@                  "node": ">= 8"              }          }, -        "node_modules/which-boxed-primitive": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", -            "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", +        "node_modules/why-is-node-running": { +            "version": "2.2.2", +            "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", +            "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",              "dev": true,              "dependencies": { -                "is-bigint": "^1.0.1", -                "is-boolean-object": "^1.1.0", -                "is-number-object": "^1.0.4", -                "is-string": "^1.0.5", -                "is-symbol": "^1.0.3" +                "siginfo": "^2.0.0", +                "stackback": "0.0.2"              }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" -            } -        }, -        "node_modules/which-typed-array": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", -            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", -            "dev": true, -            "dependencies": { -                "available-typed-arrays": "^1.0.5", -                "call-bind": "^1.0.2", -                "es-abstract": "^1.18.5", -                "foreach": "^2.0.5", -                "has-tostringtag": "^1.0.0", -                "is-typed-array": "^1.1.7" +            "bin": { +                "why-is-node-running": "cli.js"              },              "engines": { -                "node": ">= 0.4" -            }, -            "funding": { -                "url": "https://github.com/sponsors/ljharb" +                "node": ">=8"              }          },          "node_modules/wordwrap": { @@ -6155,13 +8636,15 @@              "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",              "dev": true          }, -        "node_modules/xtend": { -            "version": "4.0.2", -            "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", -            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", +        "node_modules/y18n": { +            "version": "5.0.8", +            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", +            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",              "dev": true, +            "optional": true, +            "peer": true,              "engines": { -                "node": ">=0.4" +                "node": ">=10"              }          },          "node_modules/yallist": { @@ -6170,6 +8653,26 @@              "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",              "dev": true          }, +        "node_modules/yargs": { +            "version": "17.7.2", +            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", +            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "dependencies": { +                "cliui": "^8.0.1", +                "escalade": "^3.1.1", +                "get-caller-file": "^2.0.5", +                "require-directory": "^2.1.1", +                "string-width": "^4.2.3", +                "y18n": "^5.0.5", +                "yargs-parser": "^21.1.1" +            }, +            "engines": { +                "node": ">=12" +            } +        },          "node_modules/yargs-parser": {              "version": "20.2.9",              "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", @@ -6179,6 +8682,17 @@                  "node": ">=10"              }          }, +        "node_modules/yargs/node_modules/yargs-parser": { +            "version": "21.1.1", +            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", +            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "engines": { +                "node": ">=12" +            } +        },          "node_modules/yocto-queue": {              "version": "0.1.0",              "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -6199,29 +8713,323 @@              "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",              "dev": true          }, +        "@ampproject/remapping": { +            "version": "2.2.1", +            "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", +            "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", +            "dev": true, +            "requires": { +                "@jridgewell/gen-mapping": "^0.3.0", +                "@jridgewell/trace-mapping": "^0.3.9" +            } +        },          "@babel/code-frame": { -            "version": "7.16.7", -            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", -            "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", +            "version": "7.22.13", +            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", +            "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",              "dev": true,              "requires": { -                "@babel/highlight": "^7.16.7" +                "@babel/highlight": "^7.22.13", +                "chalk": "^2.4.2" +            }, +            "dependencies": { +                "ansi-styles": { +                    "version": "3.2.1", +                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", +                    "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", +                    "dev": true, +                    "requires": { +                        "color-convert": "^1.9.0" +                    } +                }, +                "chalk": { +                    "version": "2.4.2", +                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", +                    "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", +                    "dev": true, +                    "requires": { +                        "ansi-styles": "^3.2.1", +                        "escape-string-regexp": "^1.0.5", +                        "supports-color": "^5.3.0" +                    } +                }, +                "color-convert": { +                    "version": "1.9.3", +                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", +                    "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", +                    "dev": true, +                    "requires": { +                        "color-name": "1.1.3" +                    } +                }, +                "color-name": { +                    "version": "1.1.3", +                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", +                    "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", +                    "dev": true +                }, +                "escape-string-regexp": { +                    "version": "1.0.5", +                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", +                    "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", +                    "dev": true +                }, +                "has-flag": { +                    "version": "3.0.0", +                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", +                    "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", +                    "dev": true +                }, +                "supports-color": { +                    "version": "5.5.0", +                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", +                    "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", +                    "dev": true, +                    "requires": { +                        "has-flag": "^3.0.0" +                    } +                }              }          }, +        "@babel/compat-data": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", +            "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@babel/core": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", +            "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@ampproject/remapping": "^2.2.0", +                "@babel/code-frame": "^7.22.13", +                "@babel/generator": "^7.23.0", +                "@babel/helper-compilation-targets": "^7.22.15", +                "@babel/helper-module-transforms": "^7.23.0", +                "@babel/helpers": "^7.23.2", +                "@babel/parser": "^7.23.0", +                "@babel/template": "^7.22.15", +                "@babel/traverse": "^7.23.2", +                "@babel/types": "^7.23.0", +                "convert-source-map": "^2.0.0", +                "debug": "^4.1.0", +                "gensync": "^1.0.0-beta.2", +                "json5": "^2.2.3", +                "semver": "^6.3.1" +            }, +            "dependencies": { +                "convert-source-map": { +                    "version": "2.0.0", +                    "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +                    "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                }, +                "semver": { +                    "version": "6.3.1", +                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        }, +        "@babel/generator": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", +            "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.23.0", +                "@jridgewell/gen-mapping": "^0.3.2", +                "@jridgewell/trace-mapping": "^0.3.17", +                "jsesc": "^2.5.1" +            } +        }, +        "@babel/helper-compilation-targets": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", +            "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/compat-data": "^7.22.9", +                "@babel/helper-validator-option": "^7.22.15", +                "browserslist": "^4.21.9", +                "lru-cache": "^5.1.1", +                "semver": "^6.3.1" +            }, +            "dependencies": { +                "lru-cache": { +                    "version": "5.1.1", +                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", +                    "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "yallist": "^3.0.2" +                    } +                }, +                "semver": { +                    "version": "6.3.1", +                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                }, +                "yallist": { +                    "version": "3.1.1", +                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", +                    "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        }, +        "@babel/helper-environment-visitor": { +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", +            "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@babel/helper-function-name": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", +            "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/template": "^7.22.15", +                "@babel/types": "^7.23.0" +            } +        }, +        "@babel/helper-hoist-variables": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", +            "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.22.5" +            } +        }, +        "@babel/helper-module-imports": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", +            "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.22.15" +            } +        }, +        "@babel/helper-module-transforms": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", +            "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-environment-visitor": "^7.22.20", +                "@babel/helper-module-imports": "^7.22.15", +                "@babel/helper-simple-access": "^7.22.5", +                "@babel/helper-split-export-declaration": "^7.22.6", +                "@babel/helper-validator-identifier": "^7.22.20" +            } +        }, +        "@babel/helper-plugin-utils": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", +            "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@babel/helper-simple-access": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", +            "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.22.5" +            } +        }, +        "@babel/helper-split-export-declaration": { +            "version": "7.22.6", +            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", +            "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.22.5" +            } +        }, +        "@babel/helper-string-parser": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", +            "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "@babel/helper-validator-identifier": { -            "version": "7.16.7", -            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", -            "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", +            "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",              "dev": true          }, +        "@babel/helper-validator-option": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", +            "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@babel/helpers": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", +            "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/template": "^7.22.15", +                "@babel/traverse": "^7.23.2", +                "@babel/types": "^7.23.0" +            } +        },          "@babel/highlight": { -            "version": "7.16.10", -            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", -            "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", +            "version": "7.22.20", +            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", +            "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",              "dev": true,              "requires": { -                "@babel/helper-validator-identifier": "^7.16.7", -                "chalk": "^2.0.0", +                "@babel/helper-validator-identifier": "^7.22.20", +                "chalk": "^2.4.2",                  "js-tokens": "^4.0.0"              },              "dependencies": { @@ -6257,19 +9065,19 @@                  "color-name": {                      "version": "1.1.3",                      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", -                    "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", +                    "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",                      "dev": true                  },                  "escape-string-regexp": {                      "version": "1.0.5",                      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", -                    "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", +                    "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",                      "dev": true                  },                  "has-flag": {                      "version": "3.0.0",                      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", -                    "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", +                    "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",                      "dev": true                  },                  "supports-color": { @@ -6283,6 +9091,230 @@                  }              }          }, +        "@babel/parser": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", +            "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@babel/plugin-syntax-async-generators": { +            "version": "7.8.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", +            "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-bigint": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", +            "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-class-properties": { +            "version": "7.12.13", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", +            "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.12.13" +            } +        }, +        "@babel/plugin-syntax-import-meta": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", +            "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.10.4" +            } +        }, +        "@babel/plugin-syntax-json-strings": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", +            "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-jsx": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", +            "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.22.5" +            } +        }, +        "@babel/plugin-syntax-logical-assignment-operators": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", +            "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.10.4" +            } +        }, +        "@babel/plugin-syntax-nullish-coalescing-operator": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", +            "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-numeric-separator": { +            "version": "7.10.4", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", +            "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.10.4" +            } +        }, +        "@babel/plugin-syntax-object-rest-spread": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", +            "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-optional-catch-binding": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", +            "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-optional-chaining": { +            "version": "7.8.3", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", +            "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.8.0" +            } +        }, +        "@babel/plugin-syntax-top-level-await": { +            "version": "7.14.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", +            "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.14.5" +            } +        }, +        "@babel/plugin-syntax-typescript": { +            "version": "7.22.5", +            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", +            "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-plugin-utils": "^7.22.5" +            } +        }, +        "@babel/template": { +            "version": "7.22.15", +            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", +            "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/code-frame": "^7.22.13", +                "@babel/parser": "^7.22.15", +                "@babel/types": "^7.22.15" +            } +        }, +        "@babel/traverse": { +            "version": "7.23.2", +            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", +            "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/code-frame": "^7.22.13", +                "@babel/generator": "^7.23.0", +                "@babel/helper-environment-visitor": "^7.22.20", +                "@babel/helper-function-name": "^7.23.0", +                "@babel/helper-hoist-variables": "^7.22.5", +                "@babel/helper-split-export-declaration": "^7.22.6", +                "@babel/parser": "^7.23.0", +                "@babel/types": "^7.23.0", +                "debug": "^4.1.0", +                "globals": "^11.1.0" +            }, +            "dependencies": { +                "globals": { +                    "version": "11.12.0", +                    "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", +                    "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        }, +        "@babel/types": { +            "version": "7.23.0", +            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", +            "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/helper-string-parser": "^7.22.5", +                "@babel/helper-validator-identifier": "^7.22.20", +                "to-fast-properties": "^2.0.0" +            } +        }, +        "@bcoe/v8-coverage": { +            "version": "0.2.3", +            "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", +            "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", +            "dev": true +        },          "@csstools/css-parser-algorithms": {              "version": "2.3.1",              "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz", @@ -6476,9 +9508,9 @@              "optional": true          },          "@eslint-community/eslint-utils": { -            "version": "4.3.0", -            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", -            "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", +            "version": "4.4.0", +            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", +            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",              "dev": true,              "requires": {                  "eslint-visitor-keys": "^3.3.0" @@ -6613,6 +9645,420 @@                  }              }          }, +        "@istanbuljs/load-nyc-config": { +            "version": "1.1.0", +            "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", +            "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "camelcase": "^5.3.1", +                "find-up": "^4.1.0", +                "get-package-type": "^0.1.0", +                "js-yaml": "^3.13.1", +                "resolve-from": "^5.0.0" +            }, +            "dependencies": { +                "argparse": { +                    "version": "1.0.10", +                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", +                    "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "sprintf-js": "~1.0.2" +                    } +                }, +                "camelcase": { +                    "version": "5.3.1", +                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", +                    "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                }, +                "find-up": { +                    "version": "4.1.0", +                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", +                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "locate-path": "^5.0.0", +                        "path-exists": "^4.0.0" +                    } +                }, +                "js-yaml": { +                    "version": "3.14.1", +                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", +                    "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "argparse": "^1.0.7", +                        "esprima": "^4.0.0" +                    } +                }, +                "locate-path": { +                    "version": "5.0.0", +                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", +                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-locate": "^4.1.0" +                    } +                }, +                "p-limit": { +                    "version": "2.3.0", +                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", +                    "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-try": "^2.0.0" +                    } +                }, +                "p-locate": { +                    "version": "4.1.0", +                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", +                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-limit": "^2.2.0" +                    } +                }, +                "resolve-from": { +                    "version": "5.0.0", +                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", +                    "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        }, +        "@istanbuljs/schema": { +            "version": "0.1.3", +            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", +            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", +            "dev": true +        }, +        "@jest/console": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", +            "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "slash": "^3.0.0" +            } +        }, +        "@jest/core": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", +            "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/console": "^29.7.0", +                "@jest/reporters": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "ansi-escapes": "^4.2.1", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "exit": "^0.1.2", +                "graceful-fs": "^4.2.9", +                "jest-changed-files": "^29.7.0", +                "jest-config": "^29.7.0", +                "jest-haste-map": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-resolve-dependencies": "^29.7.0", +                "jest-runner": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "jest-watcher": "^29.7.0", +                "micromatch": "^4.0.4", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "strip-ansi": "^6.0.0" +            } +        }, +        "@jest/environment": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", +            "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/fake-timers": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-mock": "^29.7.0" +            } +        }, +        "@jest/expect": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", +            "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "expect": "^29.7.0", +                "jest-snapshot": "^29.7.0" +            } +        }, +        "@jest/expect-utils": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", +            "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "jest-get-type": "^29.6.3" +            } +        }, +        "@jest/fake-timers": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", +            "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "@sinonjs/fake-timers": "^10.0.2", +                "@types/node": "*", +                "jest-message-util": "^29.7.0", +                "jest-mock": "^29.7.0", +                "jest-util": "^29.7.0" +            } +        }, +        "@jest/globals": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", +            "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/environment": "^29.7.0", +                "@jest/expect": "^29.7.0", +                "@jest/types": "^29.6.3", +                "jest-mock": "^29.7.0" +            } +        }, +        "@jest/reporters": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", +            "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@bcoe/v8-coverage": "^0.2.3", +                "@jest/console": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@jridgewell/trace-mapping": "^0.3.18", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "collect-v8-coverage": "^1.0.0", +                "exit": "^0.1.2", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "istanbul-lib-coverage": "^3.0.0", +                "istanbul-lib-instrument": "^6.0.0", +                "istanbul-lib-report": "^3.0.0", +                "istanbul-lib-source-maps": "^4.0.0", +                "istanbul-reports": "^3.1.3", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-worker": "^29.7.0", +                "slash": "^3.0.0", +                "string-length": "^4.0.1", +                "strip-ansi": "^6.0.0", +                "v8-to-istanbul": "^9.0.1" +            } +        }, +        "@jest/schemas": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", +            "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", +            "dev": true, +            "requires": { +                "@sinclair/typebox": "^0.27.8" +            } +        }, +        "@jest/source-map": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", +            "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jridgewell/trace-mapping": "^0.3.18", +                "callsites": "^3.0.0", +                "graceful-fs": "^4.2.9" +            } +        }, +        "@jest/test-result": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", +            "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/console": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/istanbul-lib-coverage": "^2.0.0", +                "collect-v8-coverage": "^1.0.0" +            } +        }, +        "@jest/test-sequencer": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", +            "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/test-result": "^29.7.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "slash": "^3.0.0" +            } +        }, +        "@jest/transform": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", +            "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/core": "^7.11.6", +                "@jest/types": "^29.6.3", +                "@jridgewell/trace-mapping": "^0.3.18", +                "babel-plugin-istanbul": "^6.1.1", +                "chalk": "^4.0.0", +                "convert-source-map": "^2.0.0", +                "fast-json-stable-stringify": "^2.1.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-util": "^29.7.0", +                "micromatch": "^4.0.4", +                "pirates": "^4.0.4", +                "slash": "^3.0.0", +                "write-file-atomic": "^4.0.2" +            }, +            "dependencies": { +                "convert-source-map": { +                    "version": "2.0.0", +                    "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +                    "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                }, +                "write-file-atomic": { +                    "version": "4.0.2", +                    "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", +                    "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "imurmurhash": "^0.1.4", +                        "signal-exit": "^3.0.7" +                    } +                } +            } +        }, +        "@jest/types": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", +            "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/schemas": "^29.6.3", +                "@types/istanbul-lib-coverage": "^2.0.0", +                "@types/istanbul-reports": "^3.0.0", +                "@types/node": "*", +                "@types/yargs": "^17.0.8", +                "chalk": "^4.0.0" +            } +        }, +        "@jridgewell/gen-mapping": { +            "version": "0.3.3", +            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", +            "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", +            "dev": true, +            "requires": { +                "@jridgewell/set-array": "^1.0.1", +                "@jridgewell/sourcemap-codec": "^1.4.10", +                "@jridgewell/trace-mapping": "^0.3.9" +            } +        }, +        "@jridgewell/resolve-uri": { +            "version": "3.1.1", +            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", +            "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", +            "dev": true +        }, +        "@jridgewell/set-array": { +            "version": "1.1.2", +            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", +            "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", +            "dev": true +        }, +        "@jridgewell/sourcemap-codec": { +            "version": "1.4.15", +            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", +            "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", +            "dev": true +        }, +        "@jridgewell/trace-mapping": { +            "version": "0.3.20", +            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", +            "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", +            "dev": true, +            "requires": { +                "@jridgewell/resolve-uri": "^3.1.0", +                "@jridgewell/sourcemap-codec": "^1.4.14" +            } +        },          "@nodelib/fs.scandir": {              "version": "2.1.5",              "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6665,12 +10111,149 @@                  "chalk": "^4.1.0"              }          }, +        "@sinclair/typebox": { +            "version": "0.27.8", +            "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", +            "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", +            "dev": true +        }, +        "@sinonjs/commons": { +            "version": "3.0.0", +            "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", +            "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "type-detect": "4.0.8" +            } +        }, +        "@sinonjs/fake-timers": { +            "version": "10.3.0", +            "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", +            "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@sinonjs/commons": "^3.0.0" +            } +        },          "@tootallnate/once": {              "version": "2.0.0",              "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",              "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",              "dev": true          }, +        "@types/babel__core": { +            "version": "7.20.3", +            "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", +            "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/parser": "^7.20.7", +                "@babel/types": "^7.20.7", +                "@types/babel__generator": "*", +                "@types/babel__template": "*", +                "@types/babel__traverse": "*" +            } +        }, +        "@types/babel__generator": { +            "version": "7.6.6", +            "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", +            "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.0.0" +            } +        }, +        "@types/babel__template": { +            "version": "7.4.3", +            "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", +            "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/parser": "^7.1.0", +                "@babel/types": "^7.0.0" +            } +        }, +        "@types/babel__traverse": { +            "version": "7.20.3", +            "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", +            "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/types": "^7.20.7" +            } +        }, +        "@types/chai": { +            "version": "4.3.9", +            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", +            "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", +            "dev": true +        }, +        "@types/chai-subset": { +            "version": "1.3.5", +            "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", +            "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", +            "dev": true, +            "requires": { +                "@types/chai": "*" +            } +        }, +        "@types/graceful-fs": { +            "version": "4.1.8", +            "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", +            "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@types/node": "*" +            } +        }, +        "@types/istanbul-lib-coverage": { +            "version": "2.0.5", +            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", +            "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", +            "dev": true +        }, +        "@types/istanbul-lib-report": { +            "version": "3.0.2", +            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", +            "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@types/istanbul-lib-coverage": "*" +            } +        }, +        "@types/istanbul-reports": { +            "version": "3.0.3", +            "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", +            "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@types/istanbul-lib-report": "*" +            } +        }, +        "@types/json-schema": { +            "version": "7.0.14", +            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", +            "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", +            "dev": true +        },          "@types/minimist": {              "version": "1.2.2",              "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -6692,12 +10275,134 @@              "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",              "dev": true          }, +        "@types/semver": { +            "version": "7.5.4", +            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", +            "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", +            "dev": true +        }, +        "@types/stack-utils": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", +            "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "@types/yargs": { +            "version": "17.0.29", +            "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", +            "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@types/yargs-parser": "*" +            } +        }, +        "@types/yargs-parser": { +            "version": "21.0.2", +            "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", +            "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "@ungap/structured-clone": {              "version": "1.2.0",              "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",              "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",              "dev": true          }, +        "@vitest/coverage-v8": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-0.34.6.tgz", +            "integrity": "sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==", +            "dev": true, +            "requires": { +                "@ampproject/remapping": "^2.2.1", +                "@bcoe/v8-coverage": "^0.2.3", +                "istanbul-lib-coverage": "^3.2.0", +                "istanbul-lib-report": "^3.0.1", +                "istanbul-lib-source-maps": "^4.0.1", +                "istanbul-reports": "^3.1.5", +                "magic-string": "^0.30.1", +                "picocolors": "^1.0.0", +                "std-env": "^3.3.3", +                "test-exclude": "^6.0.0", +                "v8-to-istanbul": "^9.1.0" +            } +        }, +        "@vitest/expect": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", +            "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", +            "dev": true, +            "requires": { +                "@vitest/spy": "0.34.6", +                "@vitest/utils": "0.34.6", +                "chai": "^4.3.10" +            } +        }, +        "@vitest/runner": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", +            "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", +            "dev": true, +            "requires": { +                "@vitest/utils": "0.34.6", +                "p-limit": "^4.0.0", +                "pathe": "^1.1.1" +            }, +            "dependencies": { +                "p-limit": { +                    "version": "4.0.0", +                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", +                    "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", +                    "dev": true, +                    "requires": { +                        "yocto-queue": "^1.0.0" +                    } +                }, +                "yocto-queue": { +                    "version": "1.0.0", +                    "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", +                    "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", +                    "dev": true +                } +            } +        }, +        "@vitest/snapshot": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", +            "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", +            "dev": true, +            "requires": { +                "magic-string": "^0.30.1", +                "pathe": "^1.1.1", +                "pretty-format": "^29.5.0" +            } +        }, +        "@vitest/spy": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", +            "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", +            "dev": true, +            "requires": { +                "tinyspy": "^2.1.1" +            } +        }, +        "@vitest/utils": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", +            "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", +            "dev": true, +            "requires": { +                "diff-sequences": "^29.4.3", +                "loupe": "^2.3.6", +                "pretty-format": "^29.5.0" +            } +        },          "@zip.js/zip.js": {              "version": "2.7.30",              "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.30.tgz", @@ -6723,30 +10428,11 @@              "dev": true,              "requires": {}          }, -        "acorn-node": { -            "version": "1.8.2", -            "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", -            "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", -            "dev": true, -            "requires": { -                "acorn": "^7.0.0", -                "acorn-walk": "^7.0.0", -                "xtend": "^4.0.2" -            }, -            "dependencies": { -                "acorn": { -                    "version": "7.4.1", -                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", -                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", -                    "dev": true -                }, -                "acorn-walk": { -                    "version": "7.2.0", -                    "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", -                    "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", -                    "dev": true -                } -            } +        "acorn-walk": { +            "version": "8.3.0", +            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", +            "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", +            "dev": true          },          "agent-base": {              "version": "6.0.2", @@ -6769,6 +10455,27 @@                  "uri-js": "^4.2.2"              }          }, +        "ansi-escapes": { +            "version": "4.3.2", +            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", +            "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "type-fest": "^0.21.3" +            }, +            "dependencies": { +                "type-fest": { +                    "version": "0.21.3", +                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", +                    "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        },          "ansi-regex": {              "version": "5.0.1",              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6784,6 +10491,18 @@                  "color-convert": "^2.0.1"              }          }, +        "anymatch": { +            "version": "3.1.3", +            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", +            "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "normalize-path": "^3.0.0", +                "picomatch": "^2.0.4" +            } +        },          "are-docs-informative": {              "version": "0.0.2",              "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -6808,52 +10527,11 @@              "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",              "dev": true          }, -        "asn1.js": { -            "version": "5.4.1", -            "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", -            "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", -            "dev": true, -            "requires": { -                "bn.js": "^4.0.0", -                "inherits": "^2.0.1", -                "minimalistic-assert": "^1.0.0", -                "safer-buffer": "^2.1.0" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } -        }, -        "assert": { -            "version": "1.5.0", -            "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", -            "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", -            "dev": true, -            "requires": { -                "object-assign": "^4.1.1", -                "util": "0.10.3" -            }, -            "dependencies": { -                "inherits": { -                    "version": "2.0.1", -                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", -                    "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", -                    "dev": true -                }, -                "util": { -                    "version": "0.10.3", -                    "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", -                    "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", -                    "dev": true, -                    "requires": { -                        "inherits": "2.0.1" -                    } -                } -            } +        "assertion-error": { +            "version": "1.1.0", +            "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", +            "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", +            "dev": true          },          "astral-regex": {              "version": "2.0.0", @@ -6873,241 +10551,168 @@              "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",              "dev": true          }, -        "available-typed-arrays": { -            "version": "1.0.5", -            "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", -            "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", -            "dev": true -        }, -        "balanced-match": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", -            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", -            "dev": true -        }, -        "base64-js": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", -            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", -            "dev": true -        }, -        "bn.js": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", -            "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", -            "dev": true -        }, -        "brace-expansion": { -            "version": "1.1.11", -            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", -            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", -            "dev": true, -            "requires": { -                "balanced-match": "^1.0.0", -                "concat-map": "0.0.1" -            } -        }, -        "braces": { -            "version": "3.0.2", -            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", -            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", +        "babel-jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", +            "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "fill-range": "^7.0.1" +                "@jest/transform": "^29.7.0", +                "@types/babel__core": "^7.1.14", +                "babel-plugin-istanbul": "^6.1.1", +                "babel-preset-jest": "^29.6.3", +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "slash": "^3.0.0"              }          }, -        "brorand": { -            "version": "1.1.0", -            "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", -            "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", -            "dev": true -        }, -        "browser-pack": { -            "version": "6.1.0", -            "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", -            "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", +        "babel-plugin-istanbul": { +            "version": "6.1.1", +            "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", +            "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "combine-source-map": "~0.8.0", -                "defined": "^1.0.0", -                "JSONStream": "^1.0.3", -                "safe-buffer": "^5.1.1", -                "through2": "^2.0.0", -                "umd": "^3.0.0" +                "@babel/helper-plugin-utils": "^7.0.0", +                "@istanbuljs/load-nyc-config": "^1.0.0", +                "@istanbuljs/schema": "^0.1.2", +                "istanbul-lib-instrument": "^5.0.4", +                "test-exclude": "^6.0.0" +            }, +            "dependencies": { +                "istanbul-lib-instrument": { +                    "version": "5.2.1", +                    "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", +                    "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "@babel/core": "^7.12.3", +                        "@babel/parser": "^7.14.7", +                        "@istanbuljs/schema": "^0.1.2", +                        "istanbul-lib-coverage": "^3.2.0", +                        "semver": "^6.3.0" +                    } +                }, +                "semver": { +                    "version": "6.3.1", +                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", +                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                }              }          }, -        "browser-resolve": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", -            "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", -            "dev": true, -            "requires": { -                "resolve": "^1.17.0" -            } -        }, -        "browserify": { -            "version": "17.0.0", -            "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", -            "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", -            "dev": true, -            "requires": { -                "assert": "^1.4.0", -                "browser-pack": "^6.0.1", -                "browser-resolve": "^2.0.0", -                "browserify-zlib": "~0.2.0", -                "buffer": "~5.2.1", -                "cached-path-relative": "^1.0.0", -                "concat-stream": "^1.6.0", -                "console-browserify": "^1.1.0", -                "constants-browserify": "~1.0.0", -                "crypto-browserify": "^3.0.0", -                "defined": "^1.0.0", -                "deps-sort": "^2.0.1", -                "domain-browser": "^1.2.0", -                "duplexer2": "~0.1.2", -                "events": "^3.0.0", -                "glob": "^7.1.0", -                "has": "^1.0.0", -                "htmlescape": "^1.1.0", -                "https-browserify": "^1.0.0", -                "inherits": "~2.0.1", -                "insert-module-globals": "^7.2.1", -                "JSONStream": "^1.0.3", -                "labeled-stream-splicer": "^2.0.0", -                "mkdirp-classic": "^0.5.2", -                "module-deps": "^6.2.3", -                "os-browserify": "~0.3.0", -                "parents": "^1.0.1", -                "path-browserify": "^1.0.0", -                "process": "~0.11.0", -                "punycode": "^1.3.2", -                "querystring-es3": "~0.2.0", -                "read-only-stream": "^2.0.0", -                "readable-stream": "^2.0.2", -                "resolve": "^1.1.4", -                "shasum-object": "^1.0.0", -                "shell-quote": "^1.6.1", -                "stream-browserify": "^3.0.0", -                "stream-http": "^3.0.0", -                "string_decoder": "^1.1.1", -                "subarg": "^1.0.0", -                "syntax-error": "^1.1.1", -                "through2": "^2.0.0", -                "timers-browserify": "^1.0.1", -                "tty-browserify": "0.0.1", -                "url": "~0.11.0", -                "util": "~0.12.0", -                "vm-browserify": "^1.0.0", -                "xtend": "^4.0.0" -            } -        }, -        "browserify-aes": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", -            "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", +        "babel-plugin-jest-hoist": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", +            "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "buffer-xor": "^1.0.3", -                "cipher-base": "^1.0.0", -                "create-hash": "^1.1.0", -                "evp_bytestokey": "^1.0.3", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" +                "@babel/template": "^7.3.3", +                "@babel/types": "^7.3.3", +                "@types/babel__core": "^7.1.14", +                "@types/babel__traverse": "^7.0.6"              }          }, -        "browserify-cipher": { +        "babel-preset-current-node-syntax": {              "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", -            "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", +            "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", +            "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/plugin-syntax-async-generators": "^7.8.4", +                "@babel/plugin-syntax-bigint": "^7.8.3", +                "@babel/plugin-syntax-class-properties": "^7.8.3", +                "@babel/plugin-syntax-import-meta": "^7.8.3", +                "@babel/plugin-syntax-json-strings": "^7.8.3", +                "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", +                "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", +                "@babel/plugin-syntax-numeric-separator": "^7.8.3", +                "@babel/plugin-syntax-object-rest-spread": "^7.8.3", +                "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", +                "@babel/plugin-syntax-optional-chaining": "^7.8.3", +                "@babel/plugin-syntax-top-level-await": "^7.8.3" +            } +        }, +        "babel-preset-jest": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", +            "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "browserify-aes": "^1.0.4", -                "browserify-des": "^1.0.0", -                "evp_bytestokey": "^1.0.0" +                "babel-plugin-jest-hoist": "^29.6.3", +                "babel-preset-current-node-syntax": "^1.0.0"              }          }, -        "browserify-des": { +        "balanced-match": {              "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", -            "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", -            "dev": true, -            "requires": { -                "cipher-base": "^1.0.1", -                "des.js": "^1.0.0", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.1.2" -            } +            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", +            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", +            "dev": true          }, -        "browserify-rsa": { -            "version": "4.1.0", -            "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", -            "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", +        "brace-expansion": { +            "version": "1.1.11", +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", +            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",              "dev": true,              "requires": { -                "bn.js": "^5.0.0", -                "randombytes": "^2.0.1" +                "balanced-match": "^1.0.0", +                "concat-map": "0.0.1"              }          }, -        "browserify-sign": { -            "version": "4.2.2", -            "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", -            "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", +        "braces": { +            "version": "3.0.2", +            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", +            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",              "dev": true,              "requires": { -                "bn.js": "^5.2.1", -                "browserify-rsa": "^4.1.0", -                "create-hash": "^1.2.0", -                "create-hmac": "^1.1.7", -                "elliptic": "^6.5.4", -                "inherits": "^2.0.4", -                "parse-asn1": "^5.1.6", -                "readable-stream": "^3.6.2", -                "safe-buffer": "^5.2.1" -            }, -            "dependencies": { -                "readable-stream": { -                    "version": "3.6.2", -                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", -                    "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", -                    "dev": true, -                    "requires": { -                        "inherits": "^2.0.3", -                        "string_decoder": "^1.1.1", -                        "util-deprecate": "^1.0.1" -                    } -                } +                "fill-range": "^7.0.1"              }          }, -        "browserify-zlib": { -            "version": "0.2.0", -            "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", -            "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", +        "browserslist": { +            "version": "4.22.1", +            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", +            "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "pako": "~1.0.5" +                "caniuse-lite": "^1.0.30001541", +                "electron-to-chromium": "^1.4.535", +                "node-releases": "^2.0.13", +                "update-browserslist-db": "^1.0.13"              }          }, -        "buffer": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", -            "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", +        "bser": { +            "version": "2.1.1", +            "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", +            "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "base64-js": "^1.0.2", -                "ieee754": "^1.1.4" +                "node-int64": "^0.4.0"              }          },          "buffer-from": {              "version": "1.1.2",              "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",              "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", -            "dev": true -        }, -        "buffer-xor": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", -            "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", -            "dev": true +            "dev": true, +            "optional": true, +            "peer": true          },          "builtin-modules": {              "version": "3.3.0", @@ -7115,28 +10720,12 @@              "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",              "dev": true          }, -        "builtin-status-codes": { -            "version": "3.0.0", -            "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", -            "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", -            "dev": true -        }, -        "cached-path-relative": { -            "version": "1.1.0", -            "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", -            "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", +        "cac": { +            "version": "6.7.14", +            "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", +            "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",              "dev": true          }, -        "call-bind": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", -            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", -            "dev": true, -            "requires": { -                "function-bind": "^1.1.1", -                "get-intrinsic": "^1.0.2" -            } -        },          "callsites": {              "version": "3.1.0",              "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -7169,6 +10758,29 @@                  }              }          }, +        "caniuse-lite": { +            "version": "1.0.30001561", +            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", +            "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "chai": { +            "version": "4.3.10", +            "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", +            "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", +            "dev": true, +            "requires": { +                "assertion-error": "^1.1.0", +                "check-error": "^1.0.3", +                "deep-eql": "^4.1.3", +                "get-func-name": "^2.0.2", +                "loupe": "^2.3.6", +                "pathval": "^1.1.1", +                "type-detect": "^4.0.8" +            } +        },          "chalk": {              "version": "4.1.2",              "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7179,16 +10791,83 @@                  "supports-color": "^7.1.0"              }          }, -        "cipher-base": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", -            "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", +        "char-regex": { +            "version": "1.0.2", +            "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", +            "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "check-error": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", +            "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",              "dev": true,              "requires": { -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" +                "get-func-name": "^2.0.2"              }          }, +        "ci-info": { +            "version": "3.9.0", +            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", +            "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "cjs-module-lexer": { +            "version": "1.2.3", +            "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", +            "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "cliui": { +            "version": "8.0.1", +            "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", +            "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "string-width": "^4.2.0", +                "strip-ansi": "^6.0.1", +                "wrap-ansi": "^7.0.0" +            }, +            "dependencies": { +                "wrap-ansi": { +                    "version": "7.0.0", +                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", +                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "ansi-styles": "^4.0.0", +                        "string-width": "^4.1.0", +                        "strip-ansi": "^6.0.0" +                    } +                } +            } +        }, +        "co": { +            "version": "4.6.0", +            "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", +            "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "collect-v8-coverage": { +            "version": "1.0.2", +            "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", +            "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "color-convert": {              "version": "2.0.1",              "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7210,18 +10889,6 @@              "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",              "dev": true          }, -        "combine-source-map": { -            "version": "0.8.0", -            "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", -            "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", -            "dev": true, -            "requires": { -                "convert-source-map": "~1.1.0", -                "inline-source-map": "~0.6.0", -                "lodash.memoize": "~3.0.3", -                "source-map": "~0.5.3" -            } -        },          "combined-stream": {              "version": "1.0.8",              "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -7243,36 +10910,6 @@              "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",              "dev": true          }, -        "concat-stream": { -            "version": "1.6.2", -            "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", -            "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", -            "dev": true, -            "requires": { -                "buffer-from": "^1.0.0", -                "inherits": "^2.0.3", -                "readable-stream": "^2.2.2", -                "typedarray": "^0.0.6" -            } -        }, -        "console-browserify": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", -            "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", -            "dev": true -        }, -        "constants-browserify": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", -            "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", -            "dev": true -        }, -        "convert-source-map": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", -            "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", -            "dev": true -        },          "core-util-is": {              "version": "1.0.3",              "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -7291,49 +10928,21 @@                  "path-type": "^4.0.0"              }          }, -        "create-ecdh": { -            "version": "4.0.4", -            "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", -            "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", -            "dev": true, -            "requires": { -                "bn.js": "^4.1.0", -                "elliptic": "^6.5.3" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } -        }, -        "create-hash": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", -            "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", -            "dev": true, -            "requires": { -                "cipher-base": "^1.0.1", -                "inherits": "^2.0.1", -                "md5.js": "^1.3.4", -                "ripemd160": "^2.0.1", -                "sha.js": "^2.4.0" -            } -        }, -        "create-hmac": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", -            "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", +        "create-jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", +            "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "cipher-base": "^1.0.3", -                "create-hash": "^1.1.0", -                "inherits": "^2.0.1", -                "ripemd160": "^2.0.0", -                "safe-buffer": "^5.0.1", -                "sha.js": "^2.4.8" +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "exit": "^0.1.2", +                "graceful-fs": "^4.2.9", +                "jest-config": "^29.7.0", +                "jest-util": "^29.7.0", +                "prompts": "^2.0.1"              }          },          "cross-spawn": { @@ -7347,25 +10956,6 @@                  "which": "^2.0.1"              }          }, -        "crypto-browserify": { -            "version": "3.12.0", -            "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", -            "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", -            "dev": true, -            "requires": { -                "browserify-cipher": "^1.0.0", -                "browserify-sign": "^4.0.0", -                "create-ecdh": "^4.0.0", -                "create-hash": "^1.1.0", -                "create-hmac": "^1.1.0", -                "diffie-hellman": "^5.0.0", -                "inherits": "^2.0.1", -                "pbkdf2": "^3.0.3", -                "public-encrypt": "^4.0.0", -                "randombytes": "^2.0.0", -                "randomfill": "^1.0.3" -            } -        },          "css": {              "version": "3.0.0",              "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -7416,12 +11006,6 @@                  "rrweb-cssom": "^0.6.0"              }          }, -        "dash-ast": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", -            "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", -            "dev": true -        },          "data-urls": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", @@ -7484,6 +11068,24 @@              "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",              "dev": true          }, +        "dedent": { +            "version": "1.5.1", +            "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", +            "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": {} +        }, +        "deep-eql": { +            "version": "4.1.3", +            "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", +            "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", +            "dev": true, +            "requires": { +                "type-detect": "^4.0.0" +            } +        },          "deep-is": {              "version": "0.1.4",              "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -7496,59 +11098,19 @@              "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",              "dev": true          }, -        "define-properties": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", -            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", -            "dev": true, -            "requires": { -                "object-keys": "^1.0.12" -            } -        }, -        "defined": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", -            "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", -            "dev": true -        },          "delayed-stream": {              "version": "1.0.0",              "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",              "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",              "dev": true          }, -        "deps-sort": { -            "version": "2.0.1", -            "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", -            "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", -            "dev": true, -            "requires": { -                "JSONStream": "^1.0.3", -                "shasum-object": "^1.0.0", -                "subarg": "^1.0.0", -                "through2": "^2.0.0" -            } -        }, -        "des.js": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", -            "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.1", -                "minimalistic-assert": "^1.0.0" -            } -        }, -        "detective": { -            "version": "5.2.0", -            "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", -            "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", +        "detect-newline": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", +            "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",              "dev": true, -            "requires": { -                "acorn-node": "^1.6.1", -                "defined": "^1.0.0", -                "minimist": "^1.1.1" -            } +            "optional": true, +            "peer": true          },          "dexie": {              "version": "3.2.4", @@ -7563,24 +11125,11 @@              "dev": true,              "requires": {}          }, -        "diffie-hellman": { -            "version": "5.0.3", -            "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", -            "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", -            "dev": true, -            "requires": { -                "bn.js": "^4.1.0", -                "miller-rabin": "^4.0.0", -                "randombytes": "^2.0.0" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } +        "diff-sequences": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", +            "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", +            "dev": true          },          "dir-glob": {              "version": "3.0.1", @@ -7600,12 +11149,6 @@                  "esutils": "^2.0.2"              }          }, -        "domain-browser": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", -            "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", -            "dev": true -        },          "domexception": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -7615,43 +11158,27 @@                  "webidl-conversions": "^7.0.0"              }          }, -        "duplexer2": { -            "version": "0.1.4", -            "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", -            "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", -            "dev": true, -            "requires": { -                "readable-stream": "^2.0.2" -            } -        },          "eastasianwidth": {              "version": "0.2.0",              "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",              "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",              "dev": true          }, -        "elliptic": { -            "version": "6.5.4", -            "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", -            "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", +        "electron-to-chromium": { +            "version": "1.4.576", +            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz", +            "integrity": "sha512-yXsZyXJfAqzWk1WKryr0Wl0MN2D47xodPvEEwlVePBnhU5E7raevLQR+E6b9JAD3GfL/7MbAL9ZtWQQPcLx7wA==",              "dev": true, -            "requires": { -                "bn.js": "^4.11.9", -                "brorand": "^1.1.0", -                "hash.js": "^1.0.0", -                "hmac-drbg": "^1.0.1", -                "inherits": "^2.0.4", -                "minimalistic-assert": "^1.0.1", -                "minimalistic-crypto-utils": "^1.0.1" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } +            "optional": true, +            "peer": true +        }, +        "emittery": { +            "version": "0.13.1", +            "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", +            "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", +            "dev": true, +            "optional": true, +            "peer": true          },          "emoji-regex": {              "version": "8.0.0", @@ -7674,45 +11201,6 @@                  "is-arrayish": "^0.2.1"              }          }, -        "es-abstract": { -            "version": "1.19.2", -            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", -            "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "es-to-primitive": "^1.2.1", -                "function-bind": "^1.1.1", -                "get-intrinsic": "^1.1.1", -                "get-symbol-description": "^1.0.0", -                "has": "^1.0.3", -                "has-symbols": "^1.0.3", -                "internal-slot": "^1.0.3", -                "is-callable": "^1.2.4", -                "is-negative-zero": "^2.0.2", -                "is-regex": "^1.1.4", -                "is-shared-array-buffer": "^1.0.1", -                "is-string": "^1.0.7", -                "is-weakref": "^1.0.2", -                "object-inspect": "^1.12.0", -                "object-keys": "^1.1.1", -                "object.assign": "^4.1.2", -                "string.prototype.trimend": "^1.0.4", -                "string.prototype.trimstart": "^1.0.4", -                "unbox-primitive": "^1.0.1" -            } -        }, -        "es-to-primitive": { -            "version": "1.2.1", -            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", -            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", -            "dev": true, -            "requires": { -                "is-callable": "^1.1.4", -                "is-date-object": "^1.0.1", -                "is-symbol": "^1.0.2" -            } -        },          "esbuild": {              "version": "0.19.5",              "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", @@ -7743,6 +11231,14 @@                  "@esbuild/win32-x64": "0.19.5"              }          }, +        "escalade": { +            "version": "3.1.1", +            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", +            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "escape-string-regexp": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -7865,6 +11361,73 @@              "dev": true,              "requires": {}          }, +        "eslint-plugin-vitest": { +            "version": "0.3.9", +            "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.9.tgz", +            "integrity": "sha512-ZGrz8dWFlotM5dwrsMLP4VcY5MizwKNV4JTnY0VKdnuCZ+qeEUMHf1qd8kRGQA3tqLvXcV929wt2ANkduq2Pgw==", +            "dev": true, +            "requires": { +                "@typescript-eslint/utils": "^6.9.1" +            }, +            "dependencies": { +                "@typescript-eslint/scope-manager": { +                    "version": "6.10.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", +                    "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.10.0", +                        "@typescript-eslint/visitor-keys": "6.10.0" +                    } +                }, +                "@typescript-eslint/types": { +                    "version": "6.10.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", +                    "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", +                    "dev": true +                }, +                "@typescript-eslint/typescript-estree": { +                    "version": "6.10.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", +                    "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.10.0", +                        "@typescript-eslint/visitor-keys": "6.10.0", +                        "debug": "^4.3.4", +                        "globby": "^11.1.0", +                        "is-glob": "^4.0.3", +                        "semver": "^7.5.4", +                        "ts-api-utils": "^1.0.1" +                    } +                }, +                "@typescript-eslint/utils": { +                    "version": "6.10.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", +                    "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", +                    "dev": true, +                    "requires": { +                        "@eslint-community/eslint-utils": "^4.4.0", +                        "@types/json-schema": "^7.0.12", +                        "@types/semver": "^7.5.0", +                        "@typescript-eslint/scope-manager": "6.10.0", +                        "@typescript-eslint/types": "6.10.0", +                        "@typescript-eslint/typescript-estree": "6.10.0", +                        "semver": "^7.5.4" +                    } +                }, +                "@typescript-eslint/visitor-keys": { +                    "version": "6.10.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", +                    "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.10.0", +                        "eslint-visitor-keys": "^3.4.1" +                    } +                } +            } +        },          "eslint-scope": {              "version": "7.2.2",              "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -7892,6 +11455,14 @@                  "eslint-visitor-keys": "^3.4.1"              }          }, +        "esprima": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", +            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "esquery": {              "version": "1.5.0",              "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -7922,20 +11493,46 @@              "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",              "dev": true          }, -        "events": { -            "version": "3.3.0", -            "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", -            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", -            "dev": true +        "execa": { +            "version": "5.1.1", +            "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", +            "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "cross-spawn": "^7.0.3", +                "get-stream": "^6.0.0", +                "human-signals": "^2.1.0", +                "is-stream": "^2.0.0", +                "merge-stream": "^2.0.0", +                "npm-run-path": "^4.0.1", +                "onetime": "^5.1.2", +                "signal-exit": "^3.0.3", +                "strip-final-newline": "^2.0.0" +            }          }, -        "evp_bytestokey": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", -            "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", +        "exit": { +            "version": "0.1.2", +            "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", +            "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",              "dev": true, +            "optional": true, +            "peer": true +        }, +        "expect": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", +            "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", +            "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "md5.js": "^1.3.4", -                "safe-buffer": "^5.1.1" +                "@jest/expect-utils": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0"              }          },          "fake-indexeddb": { @@ -7986,12 +11583,6 @@              "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",              "dev": true          }, -        "fast-safe-stringify": { -            "version": "2.1.1", -            "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", -            "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", -            "dev": true -        },          "fastest-levenshtein": {              "version": "1.0.16",              "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -8007,6 +11598,17 @@                  "reusify": "^1.0.4"              }          }, +        "fb-watchman": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", +            "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "bser": "2.1.1" +            } +        },          "file-entry-cache": {              "version": "6.0.1",              "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8052,12 +11654,6 @@              "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",              "dev": true          }, -        "foreach": { -            "version": "2.0.5", -            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", -            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", -            "dev": true -        },          "foreground-child": {              "version": "3.1.1",              "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -8106,32 +11702,43 @@              "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",              "dev": true          }, -        "get-assigned-identifiers": { -            "version": "1.2.0", -            "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", -            "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", +        "gensync": { +            "version": "1.0.0-beta.2", +            "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", +            "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "get-caller-file": { +            "version": "2.0.5", +            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", +            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "get-func-name": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", +            "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",              "dev": true          }, -        "get-intrinsic": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", -            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", +        "get-package-type": { +            "version": "0.1.0", +            "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", +            "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",              "dev": true, -            "requires": { -                "function-bind": "^1.1.1", -                "has": "^1.0.3", -                "has-symbols": "^1.0.1" -            } +            "optional": true, +            "peer": true          }, -        "get-symbol-description": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", -            "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", +        "get-stream": { +            "version": "6.0.1", +            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", +            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",              "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "get-intrinsic": "^1.1.1" -            } +            "optional": true, +            "peer": true          },          "glob": {              "version": "7.2.0", @@ -8216,6 +11823,14 @@              "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=",              "dev": true          }, +        "graceful-fs": { +            "version": "4.2.11", +            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", +            "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "graphemer": {              "version": "1.4.0",              "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -8258,78 +11873,12 @@                  "function-bind": "^1.1.1"              }          }, -        "has-bigints": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", -            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", -            "dev": true -        },          "has-flag": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",              "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",              "dev": true          }, -        "has-symbols": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", -            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", -            "dev": true -        }, -        "has-tostringtag": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", -            "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", -            "dev": true, -            "requires": { -                "has-symbols": "^1.0.2" -            } -        }, -        "hash-base": { -            "version": "3.1.0", -            "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", -            "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.4", -                "readable-stream": "^3.6.0", -                "safe-buffer": "^5.2.0" -            }, -            "dependencies": { -                "readable-stream": { -                    "version": "3.6.0", -                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", -                    "dev": true, -                    "requires": { -                        "inherits": "^2.0.3", -                        "string_decoder": "^1.1.1", -                        "util-deprecate": "^1.0.1" -                    } -                } -            } -        }, -        "hash.js": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", -            "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.3", -                "minimalistic-assert": "^1.0.1" -            } -        }, -        "hmac-drbg": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", -            "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", -            "dev": true, -            "requires": { -                "hash.js": "^1.0.3", -                "minimalistic-assert": "^1.0.0", -                "minimalistic-crypto-utils": "^1.0.1" -            } -        },          "hosted-git-info": {              "version": "4.1.0",              "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -8348,6 +11897,12 @@                  "whatwg-encoding": "^2.0.0"              }          }, +        "html-escaper": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", +            "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", +            "dev": true +        },          "html-tags": {              "version": "3.3.1",              "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", @@ -8406,12 +11961,6 @@                  }              }          }, -        "htmlescape": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", -            "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", -            "dev": true -        },          "http-proxy-agent": {              "version": "5.0.0",              "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -8423,12 +11972,6 @@                  "debug": "4"              }          }, -        "https-browserify": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", -            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", -            "dev": true -        },          "https-proxy-agent": {              "version": "5.0.1",              "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -8439,11 +11982,13 @@                  "debug": "4"              }          }, -        "ieee754": { -            "version": "1.2.1", -            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", -            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", -            "dev": true +        "human-signals": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", +            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", +            "dev": true, +            "optional": true, +            "peer": true          },          "ignore": {              "version": "5.2.4", @@ -8473,6 +12018,18 @@              "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",              "dev": true          }, +        "import-local": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", +            "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "pkg-dir": "^4.2.0", +                "resolve-cwd": "^3.0.0" +            } +        },          "imurmurhash": {              "version": "0.1.4",              "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -8507,85 +12064,12 @@              "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",              "dev": true          }, -        "inline-source-map": { -            "version": "0.6.2", -            "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", -            "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", -            "dev": true, -            "requires": { -                "source-map": "~0.5.3" -            } -        }, -        "insert-module-globals": { -            "version": "7.2.1", -            "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", -            "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", -            "dev": true, -            "requires": { -                "acorn-node": "^1.5.2", -                "combine-source-map": "^0.8.0", -                "concat-stream": "^1.6.1", -                "is-buffer": "^1.1.0", -                "JSONStream": "^1.0.3", -                "path-is-absolute": "^1.0.1", -                "process": "~0.11.0", -                "through2": "^2.0.0", -                "undeclared-identifiers": "^1.1.2", -                "xtend": "^4.0.0" -            } -        }, -        "internal-slot": { -            "version": "1.0.3", -            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", -            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", -            "dev": true, -            "requires": { -                "get-intrinsic": "^1.1.0", -                "has": "^1.0.3", -                "side-channel": "^1.0.4" -            } -        }, -        "is-arguments": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", -            "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" -            } -        },          "is-arrayish": {              "version": "0.2.1",              "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",              "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",              "dev": true          }, -        "is-bigint": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", -            "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", -            "dev": true, -            "requires": { -                "has-bigints": "^1.0.1" -            } -        }, -        "is-boolean-object": { -            "version": "1.1.2", -            "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", -            "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" -            } -        }, -        "is-buffer": { -            "version": "1.1.6", -            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", -            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", -            "dev": true -        },          "is-builtin-module": {              "version": "3.2.1",              "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", @@ -8595,12 +12079,6 @@                  "builtin-modules": "^3.3.0"              }          }, -        "is-callable": { -            "version": "1.2.4", -            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", -            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", -            "dev": true -        },          "is-core-module": {              "version": "2.8.1",              "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -8610,15 +12088,6 @@                  "has": "^1.0.3"              }          }, -        "is-date-object": { -            "version": "1.0.5", -            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", -            "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", -            "dev": true, -            "requires": { -                "has-tostringtag": "^1.0.0" -            } -        },          "is-extglob": {              "version": "2.1.1",              "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -8631,14 +12100,13 @@              "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",              "dev": true          }, -        "is-generator-function": { -            "version": "1.0.10", -            "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", -            "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", +        "is-generator-fn": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", +            "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",              "dev": true, -            "requires": { -                "has-tostringtag": "^1.0.0" -            } +            "optional": true, +            "peer": true          },          "is-glob": {              "version": "4.0.3", @@ -8649,27 +12117,12 @@                  "is-extglob": "^2.1.1"              }          }, -        "is-negative-zero": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", -            "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", -            "dev": true -        },          "is-number": {              "version": "7.0.0",              "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",              "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",              "dev": true          }, -        "is-number-object": { -            "version": "1.0.7", -            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", -            "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", -            "dev": true, -            "requires": { -                "has-tostringtag": "^1.0.0" -            } -        },          "is-path-inside": {              "version": "3.0.3",              "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -8694,85 +12147,567 @@              "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",              "dev": true          }, -        "is-regex": { -            "version": "1.1.4", -            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", -            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", +        "is-stream": { +            "version": "2.0.1", +            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", +            "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "isarray": { +            "version": "1.0.0", +            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", +            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", +            "dev": true +        }, +        "isexe": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", +            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", +            "dev": true +        }, +        "istanbul-lib-coverage": { +            "version": "3.2.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", +            "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==", +            "dev": true +        }, +        "istanbul-lib-instrument": { +            "version": "6.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", +            "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/core": "^7.12.3", +                "@babel/parser": "^7.14.7", +                "@istanbuljs/schema": "^0.1.2", +                "istanbul-lib-coverage": "^3.2.0", +                "semver": "^7.5.4" +            } +        }, +        "istanbul-lib-report": { +            "version": "3.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", +            "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",              "dev": true,              "requires": { -                "call-bind": "^1.0.2", -                "has-tostringtag": "^1.0.0" +                "istanbul-lib-coverage": "^3.0.0", +                "make-dir": "^4.0.0", +                "supports-color": "^7.1.0"              }          }, -        "is-shared-array-buffer": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", -            "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", +        "istanbul-lib-source-maps": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", +            "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",              "dev": true,              "requires": { -                "call-bind": "^1.0.2" +                "debug": "^4.1.1", +                "istanbul-lib-coverage": "^3.0.0", +                "source-map": "^0.6.1" +            }, +            "dependencies": { +                "source-map": { +                    "version": "0.6.1", +                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", +                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", +                    "dev": true +                }              }          }, -        "is-string": { -            "version": "1.0.7", -            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", -            "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", +        "istanbul-reports": { +            "version": "3.1.6", +            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", +            "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",              "dev": true,              "requires": { -                "has-tostringtag": "^1.0.0" +                "html-escaper": "^2.0.0", +                "istanbul-lib-report": "^3.0.0"              }          }, -        "is-symbol": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", -            "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", +        "jackspeak": { +            "version": "2.3.6", +            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", +            "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",              "dev": true,              "requires": { -                "has-symbols": "^1.0.2" +                "@isaacs/cliui": "^8.0.2", +                "@pkgjs/parseargs": "^0.11.0"              }          }, -        "is-typed-array": { -            "version": "1.1.8", -            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", -            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", +        "jest": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", +            "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "available-typed-arrays": "^1.0.5", -                "call-bind": "^1.0.2", -                "es-abstract": "^1.18.5", -                "foreach": "^2.0.5", -                "has-tostringtag": "^1.0.0" +                "@jest/core": "^29.7.0", +                "@jest/types": "^29.6.3", +                "import-local": "^3.0.2", +                "jest-cli": "^29.7.0"              }          }, -        "is-weakref": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", -            "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", +        "jest-changed-files": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", +            "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "call-bind": "^1.0.2" +                "execa": "^5.0.0", +                "jest-util": "^29.7.0", +                "p-limit": "^3.1.0"              }          }, -        "isarray": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", -            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", -            "dev": true +        "jest-circus": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", +            "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/environment": "^29.7.0", +                "@jest/expect": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "co": "^4.6.0", +                "dedent": "^1.0.0", +                "is-generator-fn": "^2.0.0", +                "jest-each": "^29.7.0", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "p-limit": "^3.1.0", +                "pretty-format": "^29.7.0", +                "pure-rand": "^6.0.0", +                "slash": "^3.0.0", +                "stack-utils": "^2.0.3" +            } +        }, +        "jest-cli": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", +            "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/core": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "create-jest": "^29.7.0", +                "exit": "^0.1.2", +                "import-local": "^3.0.2", +                "jest-config": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "yargs": "^17.3.1" +            }          }, -        "isexe": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", -            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", -            "dev": true +        "jest-config": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", +            "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/core": "^7.11.6", +                "@jest/test-sequencer": "^29.7.0", +                "@jest/types": "^29.6.3", +                "babel-jest": "^29.7.0", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "deepmerge": "^4.2.2", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "jest-circus": "^29.7.0", +                "jest-environment-node": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-runner": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "micromatch": "^4.0.4", +                "parse-json": "^5.2.0", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "strip-json-comments": "^3.1.1" +            }          }, -        "jackspeak": { -            "version": "2.3.6", -            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", -            "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", +        "jest-diff": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", +            "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "@isaacs/cliui": "^8.0.2", -                "@pkgjs/parseargs": "^0.11.0" +                "chalk": "^4.0.0", +                "diff-sequences": "^29.6.3", +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            } +        }, +        "jest-docblock": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", +            "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "detect-newline": "^3.0.0" +            } +        }, +        "jest-each": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", +            "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "chalk": "^4.0.0", +                "jest-get-type": "^29.6.3", +                "jest-util": "^29.7.0", +                "pretty-format": "^29.7.0" +            } +        }, +        "jest-environment-node": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", +            "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/environment": "^29.7.0", +                "@jest/fake-timers": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-mock": "^29.7.0", +                "jest-util": "^29.7.0" +            } +        }, +        "jest-get-type": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", +            "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "jest-haste-map": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", +            "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "@types/graceful-fs": "^4.1.3", +                "@types/node": "*", +                "anymatch": "^3.0.3", +                "fb-watchman": "^2.0.0", +                "fsevents": "^2.3.2", +                "graceful-fs": "^4.2.9", +                "jest-regex-util": "^29.6.3", +                "jest-util": "^29.7.0", +                "jest-worker": "^29.7.0", +                "micromatch": "^4.0.4", +                "walker": "^1.0.8" +            } +        }, +        "jest-leak-detector": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", +            "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            } +        }, +        "jest-matcher-utils": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", +            "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "chalk": "^4.0.0", +                "jest-diff": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "pretty-format": "^29.7.0" +            } +        }, +        "jest-message-util": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", +            "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/code-frame": "^7.12.13", +                "@jest/types": "^29.6.3", +                "@types/stack-utils": "^2.0.0", +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "micromatch": "^4.0.4", +                "pretty-format": "^29.7.0", +                "slash": "^3.0.0", +                "stack-utils": "^2.0.3" +            } +        }, +        "jest-mock": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", +            "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "jest-util": "^29.7.0" +            } +        }, +        "jest-pnp-resolver": { +            "version": "1.2.3", +            "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", +            "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": {} +        }, +        "jest-regex-util": { +            "version": "29.6.3", +            "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", +            "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "jest-resolve": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", +            "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "chalk": "^4.0.0", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-pnp-resolver": "^1.2.2", +                "jest-util": "^29.7.0", +                "jest-validate": "^29.7.0", +                "resolve": "^1.20.0", +                "resolve.exports": "^2.0.0", +                "slash": "^3.0.0" +            } +        }, +        "jest-resolve-dependencies": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", +            "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "jest-regex-util": "^29.6.3", +                "jest-snapshot": "^29.7.0" +            } +        }, +        "jest-runner": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", +            "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/console": "^29.7.0", +                "@jest/environment": "^29.7.0", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "emittery": "^0.13.1", +                "graceful-fs": "^4.2.9", +                "jest-docblock": "^29.7.0", +                "jest-environment-node": "^29.7.0", +                "jest-haste-map": "^29.7.0", +                "jest-leak-detector": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-resolve": "^29.7.0", +                "jest-runtime": "^29.7.0", +                "jest-util": "^29.7.0", +                "jest-watcher": "^29.7.0", +                "jest-worker": "^29.7.0", +                "p-limit": "^3.1.0", +                "source-map-support": "0.5.13" +            } +        }, +        "jest-runtime": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", +            "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/environment": "^29.7.0", +                "@jest/fake-timers": "^29.7.0", +                "@jest/globals": "^29.7.0", +                "@jest/source-map": "^29.6.3", +                "@jest/test-result": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "cjs-module-lexer": "^1.0.0", +                "collect-v8-coverage": "^1.0.0", +                "glob": "^7.1.3", +                "graceful-fs": "^4.2.9", +                "jest-haste-map": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-mock": "^29.7.0", +                "jest-regex-util": "^29.6.3", +                "jest-resolve": "^29.7.0", +                "jest-snapshot": "^29.7.0", +                "jest-util": "^29.7.0", +                "slash": "^3.0.0", +                "strip-bom": "^4.0.0" +            } +        }, +        "jest-snapshot": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", +            "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@babel/core": "^7.11.6", +                "@babel/generator": "^7.7.2", +                "@babel/plugin-syntax-jsx": "^7.7.2", +                "@babel/plugin-syntax-typescript": "^7.7.2", +                "@babel/types": "^7.3.3", +                "@jest/expect-utils": "^29.7.0", +                "@jest/transform": "^29.7.0", +                "@jest/types": "^29.6.3", +                "babel-preset-current-node-syntax": "^1.0.0", +                "chalk": "^4.0.0", +                "expect": "^29.7.0", +                "graceful-fs": "^4.2.9", +                "jest-diff": "^29.7.0", +                "jest-get-type": "^29.6.3", +                "jest-matcher-utils": "^29.7.0", +                "jest-message-util": "^29.7.0", +                "jest-util": "^29.7.0", +                "natural-compare": "^1.4.0", +                "pretty-format": "^29.7.0", +                "semver": "^7.5.3" +            } +        }, +        "jest-util": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", +            "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "chalk": "^4.0.0", +                "ci-info": "^3.2.0", +                "graceful-fs": "^4.2.9", +                "picomatch": "^2.2.3" +            } +        }, +        "jest-validate": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", +            "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/types": "^29.6.3", +                "camelcase": "^6.2.0", +                "chalk": "^4.0.0", +                "jest-get-type": "^29.6.3", +                "leven": "^3.1.0", +                "pretty-format": "^29.7.0" +            } +        }, +        "jest-watcher": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", +            "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@jest/test-result": "^29.7.0", +                "@jest/types": "^29.6.3", +                "@types/node": "*", +                "ansi-escapes": "^4.2.1", +                "chalk": "^4.0.0", +                "emittery": "^0.13.1", +                "jest-util": "^29.7.0", +                "string-length": "^4.0.1" +            } +        }, +        "jest-worker": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", +            "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "@types/node": "*", +                "jest-util": "^29.7.0", +                "merge-stream": "^2.0.0", +                "supports-color": "^8.0.0" +            }, +            "dependencies": { +                "supports-color": { +                    "version": "8.1.1", +                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", +                    "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "has-flag": "^4.0.0" +                    } +                }              }          },          "js-tokens": { @@ -8827,6 +12762,14 @@                  "xml-name-validator": "^4.0.0"              }          }, +        "jsesc": { +            "version": "2.5.2", +            "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", +            "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "json-buffer": {              "version": "3.0.1",              "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -8851,6 +12794,14 @@              "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",              "dev": true          }, +        "json5": { +            "version": "2.2.3", +            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", +            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "jsonc-eslint-parser": {              "version": "2.1.0",              "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz", @@ -8863,22 +12814,12 @@                  "semver": "^7.3.5"              }          }, -        "jsonparse": { -            "version": "1.3.1", -            "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", -            "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", +        "jsonc-parser": { +            "version": "3.2.0", +            "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", +            "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",              "dev": true          }, -        "JSONStream": { -            "version": "1.3.5", -            "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", -            "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", -            "dev": true, -            "requires": { -                "jsonparse": "^1.2.0", -                "through": ">=2.2.7 <3" -            } -        },          "jszip": {              "version": "3.10.1",              "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -8918,15 +12859,13 @@              "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==",              "dev": true          }, -        "labeled-stream-splicer": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", -            "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", +        "leven": { +            "version": "3.1.0", +            "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", +            "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",              "dev": true, -            "requires": { -                "inherits": "^2.0.1", -                "stream-splicer": "^2.0.0" -            } +            "optional": true, +            "peer": true          },          "levn": {              "version": "0.4.1", @@ -8953,6 +12892,12 @@              "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",              "dev": true          }, +        "local-pkg": { +            "version": "0.4.3", +            "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", +            "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", +            "dev": true +        },          "locate-path": {              "version": "6.0.0",              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8962,12 +12907,6 @@                  "p-locate": "^5.0.0"              }          }, -        "lodash.memoize": { -            "version": "3.0.4", -            "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", -            "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", -            "dev": true -        },          "lodash.merge": {              "version": "4.6.2",              "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -8980,6 +12919,15 @@              "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",              "dev": true          }, +        "loupe": { +            "version": "2.3.7", +            "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", +            "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", +            "dev": true, +            "requires": { +                "get-func-name": "^2.0.1" +            } +        },          "lru-cache": {              "version": "6.0.0",              "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -8989,6 +12937,35 @@                  "yallist": "^4.0.0"              }          }, +        "magic-string": { +            "version": "0.30.5", +            "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", +            "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", +            "dev": true, +            "requires": { +                "@jridgewell/sourcemap-codec": "^1.4.15" +            } +        }, +        "make-dir": { +            "version": "4.0.0", +            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", +            "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", +            "dev": true, +            "requires": { +                "semver": "^7.5.3" +            } +        }, +        "makeerror": { +            "version": "1.0.12", +            "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", +            "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "tmpl": "1.0.5" +            } +        },          "map-obj": {              "version": "4.3.0",              "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -9001,17 +12978,6 @@              "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",              "dev": true          }, -        "md5.js": { -            "version": "1.3.5", -            "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", -            "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", -            "dev": true, -            "requires": { -                "hash-base": "^3.0.0", -                "inherits": "^2.0.1", -                "safe-buffer": "^5.1.2" -            } -        },          "mdn-data": {              "version": "2.0.30",              "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -9046,6 +13012,14 @@                  }              }          }, +        "merge-stream": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", +            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "merge2": {              "version": "1.4.1",              "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9062,24 +13036,6 @@                  "picomatch": "^2.3.1"              }          }, -        "miller-rabin": { -            "version": "4.0.1", -            "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", -            "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", -            "dev": true, -            "requires": { -                "bn.js": "^4.0.0", -                "brorand": "^1.0.1" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } -        },          "mime-db": {              "version": "1.52.0",              "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -9095,24 +13051,20 @@                  "mime-db": "1.52.0"              }          }, +        "mimic-fn": { +            "version": "2.1.0", +            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", +            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "min-indent": {              "version": "1.0.1",              "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",              "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",              "dev": true          }, -        "minimalistic-assert": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", -            "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", -            "dev": true -        }, -        "minimalistic-crypto-utils": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", -            "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", -            "dev": true -        },          "minimatch": {              "version": "3.1.2",              "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9145,33 +13097,16 @@              "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==",              "dev": true          }, -        "mkdirp-classic": { -            "version": "0.5.3", -            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", -            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", -            "dev": true -        }, -        "module-deps": { -            "version": "6.2.3", -            "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", -            "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", +        "mlly": { +            "version": "1.4.2", +            "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", +            "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==",              "dev": true,              "requires": { -                "browser-resolve": "^2.0.0", -                "cached-path-relative": "^1.0.2", -                "concat-stream": "~1.6.0", -                "defined": "^1.0.0", -                "detective": "^5.2.0", -                "duplexer2": "^0.1.2", -                "inherits": "^2.0.1", -                "JSONStream": "^1.0.3", -                "parents": "^1.0.0", -                "readable-stream": "^2.0.2", -                "resolve": "^1.4.0", -                "stream-combiner2": "^1.1.1", -                "subarg": "^1.0.0", -                "through2": "^2.0.0", -                "xtend": "^4.0.0" +                "acorn": "^8.10.0", +                "pathe": "^1.1.1", +                "pkg-types": "^1.0.3", +                "ufo": "^1.3.0"              }          },          "ms": { @@ -9198,6 +13133,22 @@              "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",              "dev": true          }, +        "node-int64": { +            "version": "0.4.0", +            "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", +            "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "node-releases": { +            "version": "2.0.13", +            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", +            "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "normalize-package-data": {              "version": "3.0.3",              "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -9216,42 +13167,23 @@              "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",              "dev": true          }, +        "npm-run-path": { +            "version": "4.0.1", +            "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", +            "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "path-key": "^3.0.0" +            } +        },          "nwsapi": {              "version": "2.2.7",              "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",              "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",              "dev": true          }, -        "object-assign": { -            "version": "4.1.1", -            "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", -            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", -            "dev": true -        }, -        "object-inspect": { -            "version": "1.12.0", -            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", -            "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", -            "dev": true -        }, -        "object-keys": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", -            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", -            "dev": true -        }, -        "object.assign": { -            "version": "4.1.2", -            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", -            "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.0", -                "define-properties": "^1.1.3", -                "has-symbols": "^1.0.1", -                "object-keys": "^1.1.1" -            } -        },          "once": {              "version": "1.4.0",              "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9261,6 +13193,17 @@                  "wrappy": "1"              }          }, +        "onetime": { +            "version": "5.1.2", +            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", +            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "mimic-fn": "^2.1.0" +            } +        },          "optionator": {              "version": "0.9.3",              "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -9275,12 +13218,6 @@                  "type-check": "^0.4.0"              }          }, -        "os-browserify": { -            "version": "0.3.0", -            "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", -            "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", -            "dev": true -        },          "p-limit": {              "version": "3.1.0",              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9299,6 +13236,14 @@                  "p-limit": "^3.0.2"              }          }, +        "p-try": { +            "version": "2.2.0", +            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", +            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "pako": {              "version": "1.0.11",              "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -9314,28 +13259,6 @@                  "callsites": "^3.0.0"              }          }, -        "parents": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", -            "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", -            "dev": true, -            "requires": { -                "path-platform": "~0.11.15" -            } -        }, -        "parse-asn1": { -            "version": "5.1.6", -            "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", -            "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", -            "dev": true, -            "requires": { -                "asn1.js": "^5.2.0", -                "browserify-aes": "^1.0.0", -                "evp_bytestokey": "^1.0.0", -                "pbkdf2": "^3.0.3", -                "safe-buffer": "^5.1.1" -            } -        },          "parse-json": {              "version": "5.2.0",              "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9357,12 +13280,6 @@                  "entities": "^4.4.0"              }          }, -        "path-browserify": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", -            "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", -            "dev": true -        },          "path-exists": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9385,13 +13302,9 @@              "version": "1.0.7",              "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",              "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", -            "dev": true -        }, -        "path-platform": { -            "version": "0.11.15", -            "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", -            "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", -            "dev": true +            "dev": true, +            "optional": true, +            "peer": true          },          "path-scurry": {              "version": "1.10.1", @@ -9417,18 +13330,17 @@              "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",              "dev": true          }, -        "pbkdf2": { -            "version": "3.1.2", -            "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", -            "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", -            "dev": true, -            "requires": { -                "create-hash": "^1.1.2", -                "create-hmac": "^1.1.4", -                "ripemd160": "^2.0.1", -                "safe-buffer": "^5.0.1", -                "sha.js": "^2.4.8" -            } +        "pathe": { +            "version": "1.1.1", +            "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", +            "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", +            "dev": true +        }, +        "pathval": { +            "version": "1.1.1", +            "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", +            "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", +            "dev": true          },          "picocolors": {              "version": "1.0.0", @@ -9442,6 +13354,83 @@              "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",              "dev": true          }, +        "pirates": { +            "version": "4.0.6", +            "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", +            "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "pkg-dir": { +            "version": "4.2.0", +            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", +            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "find-up": "^4.0.0" +            }, +            "dependencies": { +                "find-up": { +                    "version": "4.1.0", +                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", +                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "locate-path": "^5.0.0", +                        "path-exists": "^4.0.0" +                    } +                }, +                "locate-path": { +                    "version": "5.0.0", +                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", +                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-locate": "^4.1.0" +                    } +                }, +                "p-limit": { +                    "version": "2.3.0", +                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", +                    "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-try": "^2.0.0" +                    } +                }, +                "p-locate": { +                    "version": "4.1.0", +                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", +                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", +                    "dev": true, +                    "optional": true, +                    "peer": true, +                    "requires": { +                        "p-limit": "^2.2.0" +                    } +                } +            } +        }, +        "pkg-types": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", +            "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", +            "dev": true, +            "requires": { +                "jsonc-parser": "^3.2.0", +                "mlly": "^1.2.0", +                "pathe": "^1.1.0" +            } +        },          "playwright": {              "version": "1.39.0",              "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", @@ -9504,11 +13493,24 @@              "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",              "dev": true          }, -        "process": { -            "version": "0.11.10", -            "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", -            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", -            "dev": true +        "pretty-format": { +            "version": "29.7.0", +            "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", +            "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", +            "dev": true, +            "requires": { +                "@jest/schemas": "^29.6.3", +                "ansi-styles": "^5.0.0", +                "react-is": "^18.0.0" +            }, +            "dependencies": { +                "ansi-styles": { +                    "version": "5.2.0", +                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", +                    "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", +                    "dev": true +                } +            }          },          "process-nextick-args": {              "version": "2.0.1", @@ -9540,45 +13542,13 @@              "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",              "dev": true          }, -        "public-encrypt": { -            "version": "4.0.3", -            "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", -            "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", +        "pure-rand": { +            "version": "6.0.4", +            "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", +            "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==",              "dev": true, -            "requires": { -                "bn.js": "^4.1.0", -                "browserify-rsa": "^4.0.0", -                "create-hash": "^1.1.0", -                "parse-asn1": "^5.0.0", -                "randombytes": "^2.0.1", -                "safe-buffer": "^5.1.2" -            }, -            "dependencies": { -                "bn.js": { -                    "version": "4.12.0", -                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", -                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", -                    "dev": true -                } -            } -        }, -        "punycode": { -            "version": "1.4.1", -            "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", -            "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", -            "dev": true -        }, -        "querystring": { -            "version": "0.2.0", -            "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", -            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", -            "dev": true -        }, -        "querystring-es3": { -            "version": "0.2.1", -            "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", -            "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", -            "dev": true +            "optional": true, +            "peer": true          },          "querystringify": {              "version": "2.2.0", @@ -9598,33 +13568,11 @@              "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",              "dev": true          }, -        "randombytes": { -            "version": "2.1.0", -            "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", -            "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", -            "dev": true, -            "requires": { -                "safe-buffer": "^5.1.0" -            } -        }, -        "randomfill": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", -            "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", -            "dev": true, -            "requires": { -                "randombytes": "^2.0.5", -                "safe-buffer": "^5.1.0" -            } -        }, -        "read-only-stream": { -            "version": "2.0.0", -            "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", -            "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", -            "dev": true, -            "requires": { -                "readable-stream": "^2.0.2" -            } +        "react-is": { +            "version": "18.2.0", +            "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", +            "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", +            "dev": true          },          "read-pkg": {              "version": "6.0.0", @@ -9707,6 +13655,14 @@                  "strip-indent": "^4.0.0"              }          }, +        "require-directory": { +            "version": "2.1.1", +            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", +            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "require-from-string": {              "version": "2.0.2",              "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -9724,18 +13680,49 @@              "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",              "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": {                  "is-core-module": "^2.8.1",                  "path-parse": "^1.0.7",                  "supports-preserve-symlinks-flag": "^1.0.0"              }          }, +        "resolve-cwd": { +            "version": "3.0.0", +            "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", +            "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "resolve-from": "^5.0.0" +            }, +            "dependencies": { +                "resolve-from": { +                    "version": "5.0.0", +                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", +                    "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        },          "resolve-from": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",              "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",              "dev": true          }, +        "resolve.exports": { +            "version": "2.0.2", +            "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", +            "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "reusify": {              "version": "1.0.4",              "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -9751,14 +13738,13 @@                  "glob": "^7.1.3"              }          }, -        "ripemd160": { -            "version": "2.0.2", -            "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", -            "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", +        "rollup": { +            "version": "3.29.4", +            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", +            "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",              "dev": true,              "requires": { -                "hash-base": "^3.0.0", -                "inherits": "^2.0.1" +                "fsevents": "~2.3.2"              }          },          "rrweb-cssom": { @@ -9776,12 +13762,6 @@                  "queue-microtask": "^1.2.2"              }          }, -        "safe-buffer": { -            "version": "5.2.1", -            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", -            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", -            "dev": true -        },          "safer-buffer": {              "version": "2.1.2",              "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -9812,25 +13792,6 @@              "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",              "dev": true          }, -        "sha.js": { -            "version": "2.4.11", -            "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", -            "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.1", -                "safe-buffer": "^5.0.1" -            } -        }, -        "shasum-object": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", -            "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", -            "dev": true, -            "requires": { -                "fast-safe-stringify": "^2.0.7" -            } -        },          "shebang-command": {              "version": "2.0.0",              "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9846,28 +13807,19 @@              "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",              "dev": true          }, -        "shell-quote": { -            "version": "1.7.3", -            "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", -            "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", +        "siginfo": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", +            "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",              "dev": true          }, -        "side-channel": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", -            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", +        "signal-exit": { +            "version": "3.0.7", +            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", +            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",              "dev": true, -            "requires": { -                "call-bind": "^1.0.0", -                "get-intrinsic": "^1.0.2", -                "object-inspect": "^1.9.0" -            } -        }, -        "simple-concat": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", -            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", -            "dev": true +            "optional": true, +            "peer": true          },          "sisteransi": {              "version": "1.0.5", @@ -9892,12 +13844,6 @@                  "is-fullwidth-code-point": "^3.0.0"              }          }, -        "source-map": { -            "version": "0.5.7", -            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", -            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", -            "dev": true -        },          "source-map-js": {              "version": "1.0.2",              "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -9914,6 +13860,28 @@                  "decode-uri-component": "^0.2.0"              }          }, +        "source-map-support": { +            "version": "0.5.13", +            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", +            "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "buffer-from": "^1.0.0", +                "source-map": "^0.6.0" +            }, +            "dependencies": { +                "source-map": { +                    "version": "0.6.1", +                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", +                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        },          "spdx-correct": {              "version": "3.2.0",              "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -9946,81 +13914,57 @@              "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==",              "dev": true          }, -        "stream-browserify": { -            "version": "3.0.0", -            "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", -            "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", -            "dev": true, -            "requires": { -                "inherits": "~2.0.4", -                "readable-stream": "^3.5.0" -            }, -            "dependencies": { -                "readable-stream": { -                    "version": "3.6.0", -                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", -                    "dev": true, -                    "requires": { -                        "inherits": "^2.0.3", -                        "string_decoder": "^1.1.1", -                        "util-deprecate": "^1.0.1" -                    } -                } -            } -        }, -        "stream-combiner2": { -            "version": "1.1.1", -            "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", -            "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", +        "sprintf-js": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", +            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",              "dev": true, -            "requires": { -                "duplexer2": "~0.1.0", -                "readable-stream": "^2.0.2" -            } +            "optional": true, +            "peer": true          }, -        "stream-http": { -            "version": "3.2.0", -            "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", -            "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", +        "stack-utils": { +            "version": "2.0.6", +            "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", +            "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "builtin-status-codes": "^3.0.0", -                "inherits": "^2.0.4", -                "readable-stream": "^3.6.0", -                "xtend": "^4.0.2" +                "escape-string-regexp": "^2.0.0"              },              "dependencies": { -                "readable-stream": { -                    "version": "3.6.0", -                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", -                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", +                "escape-string-regexp": { +                    "version": "2.0.0", +                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", +                    "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",                      "dev": true, -                    "requires": { -                        "inherits": "^2.0.3", -                        "string_decoder": "^1.1.1", -                        "util-deprecate": "^1.0.1" -                    } +                    "optional": true, +                    "peer": true                  }              }          }, -        "stream-splicer": { -            "version": "2.0.1", -            "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", -            "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.1", -                "readable-stream": "^2.0.2" -            } +        "stackback": { +            "version": "0.0.2", +            "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", +            "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", +            "dev": true          }, -        "string_decoder": { -            "version": "1.3.0", -            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", -            "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", +        "std-env": { +            "version": "3.4.3", +            "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", +            "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", +            "dev": true +        }, +        "string-length": { +            "version": "4.0.2", +            "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", +            "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",              "dev": true, +            "optional": true, +            "peer": true,              "requires": { -                "safe-buffer": "~5.2.0" +                "char-regex": "^1.0.2", +                "strip-ansi": "^6.0.0"              }          },          "string-width": { @@ -10045,26 +13989,6 @@                  "strip-ansi": "^6.0.1"              }          }, -        "string.prototype.trimend": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", -            "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "define-properties": "^1.1.3" -            } -        }, -        "string.prototype.trimstart": { -            "version": "1.0.4", -            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", -            "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", -            "dev": true, -            "requires": { -                "call-bind": "^1.0.2", -                "define-properties": "^1.1.3" -            } -        },          "strip-ansi": {              "version": "6.0.1",              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -10083,6 +14007,22 @@                  "ansi-regex": "^5.0.1"              }          }, +        "strip-bom": { +            "version": "4.0.0", +            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", +            "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", +            "dev": true, +            "optional": true, +            "peer": true +        }, +        "strip-final-newline": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", +            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", +            "dev": true, +            "optional": true, +            "peer": true +        },          "strip-indent": {              "version": "4.0.0",              "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", @@ -10098,6 +14038,15 @@              "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",              "dev": true          }, +        "strip-literal": { +            "version": "1.3.0", +            "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", +            "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", +            "dev": true, +            "requires": { +                "acorn": "^8.10.0" +            } +        },          "style-search": {              "version": "0.1.0",              "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -10182,15 +14131,6 @@              "dev": true,              "requires": {}          }, -        "subarg": { -            "version": "1.0.0", -            "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", -            "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", -            "dev": true, -            "requires": { -                "minimist": "^1.1.0" -            } -        },          "supports-color": {              "version": "7.2.0",              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10214,7 +14154,9 @@              "version": "1.0.0",              "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",              "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", -            "dev": true +            "dev": true, +            "optional": true, +            "peer": true          },          "svg-tags": {              "version": "1.0.0", @@ -10228,15 +14170,6 @@              "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",              "dev": true          }, -        "syntax-error": { -            "version": "1.4.0", -            "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", -            "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", -            "dev": true, -            "requires": { -                "acorn-node": "^1.2.0" -            } -        },          "table": {              "version": "6.8.1",              "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", @@ -10250,36 +14183,56 @@                  "strip-ansi": "^6.0.1"              }          }, +        "test-exclude": { +            "version": "6.0.0", +            "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", +            "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", +            "dev": true, +            "requires": { +                "@istanbuljs/schema": "^0.1.2", +                "glob": "^7.1.4", +                "minimatch": "^3.0.4" +            } +        },          "text-table": {              "version": "0.2.0",              "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",              "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",              "dev": true          }, -        "through": { -            "version": "2.3.8", -            "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", -            "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", +        "tinybench": { +            "version": "2.5.1", +            "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", +            "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==",              "dev": true          }, -        "through2": { -            "version": "2.0.5", -            "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", -            "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", +        "tinypool": { +            "version": "0.7.0", +            "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", +            "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", +            "dev": true +        }, +        "tinyspy": { +            "version": "2.2.0", +            "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", +            "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", +            "dev": true +        }, +        "tmpl": { +            "version": "1.0.5", +            "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", +            "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",              "dev": true, -            "requires": { -                "readable-stream": "~2.3.6", -                "xtend": "~4.0.1" -            } +            "optional": true, +            "peer": true          }, -        "timers-browserify": { -            "version": "1.4.2", -            "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", -            "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", +        "to-fast-properties": { +            "version": "2.0.0", +            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", +            "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",              "dev": true, -            "requires": { -                "process": "~0.11.0" -            } +            "optional": true, +            "peer": true          },          "to-regex-range": {              "version": "5.0.1", @@ -10333,11 +14286,12 @@              "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==",              "dev": true          }, -        "tty-browserify": { -            "version": "0.0.1", -            "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", -            "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", -            "dev": true +        "ts-api-utils": { +            "version": "1.0.3", +            "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", +            "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", +            "dev": true, +            "requires": {}          },          "type-check": {              "version": "0.4.0", @@ -10348,16 +14302,29 @@                  "prelude-ls": "^1.2.1"              }          }, +        "type-detect": { +            "version": "4.0.8", +            "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", +            "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", +            "dev": true +        },          "type-fest": {              "version": "0.20.2",              "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",              "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",              "dev": true          }, -        "typedarray": { -            "version": "0.0.6", -            "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", -            "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", +        "typescript": { +            "version": "5.2.2", +            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", +            "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", +            "dev": true, +            "peer": true +        }, +        "ufo": { +            "version": "1.3.1", +            "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.1.tgz", +            "integrity": "sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==",              "dev": true          },          "uglify-js": { @@ -10367,37 +14334,6 @@              "dev": true,              "optional": true          }, -        "umd": { -            "version": "3.0.3", -            "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", -            "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", -            "dev": true -        }, -        "unbox-primitive": { -            "version": "1.0.1", -            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", -            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", -            "dev": true, -            "requires": { -                "function-bind": "^1.1.1", -                "has-bigints": "^1.0.1", -                "has-symbols": "^1.0.2", -                "which-boxed-primitive": "^1.0.2" -            } -        }, -        "undeclared-identifiers": { -            "version": "1.1.3", -            "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", -            "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", -            "dev": true, -            "requires": { -                "acorn-node": "^1.3.0", -                "dash-ast": "^1.0.0", -                "get-assigned-identifiers": "^1.2.0", -                "simple-concat": "^1.0.0", -                "xtend": "^4.0.1" -            } -        },          "undici-types": {              "version": "5.26.5",              "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -10410,6 +14346,18 @@              "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",              "dev": true          }, +        "update-browserslist-db": { +            "version": "1.0.13", +            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", +            "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "escalade": "^3.1.1", +                "picocolors": "^1.0.0" +            } +        },          "uri-js": {              "version": "4.4.1",              "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10427,24 +14375,6 @@                  }              }          }, -        "url": { -            "version": "0.11.0", -            "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", -            "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", -            "dev": true, -            "requires": { -                "punycode": "1.3.2", -                "querystring": "0.2.0" -            }, -            "dependencies": { -                "punycode": { -                    "version": "1.3.2", -                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", -                    "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", -                    "dev": true -                } -            } -        },          "url-parse": {              "version": "1.5.10",              "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -10455,26 +14385,31 @@                  "requires-port": "^1.0.0"              }          }, -        "util": { -            "version": "0.12.4", -            "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", -            "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", -            "dev": true, -            "requires": { -                "inherits": "^2.0.3", -                "is-arguments": "^1.0.4", -                "is-generator-function": "^1.0.7", -                "is-typed-array": "^1.1.3", -                "safe-buffer": "^5.1.2", -                "which-typed-array": "^1.1.2" -            } -        },          "util-deprecate": {              "version": "1.0.2",              "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",              "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",              "dev": true          }, +        "v8-to-istanbul": { +            "version": "9.1.3", +            "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", +            "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", +            "dev": true, +            "requires": { +                "@jridgewell/trace-mapping": "^0.3.12", +                "@types/istanbul-lib-coverage": "^2.0.1", +                "convert-source-map": "^2.0.0" +            }, +            "dependencies": { +                "convert-source-map": { +                    "version": "2.0.0", +                    "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", +                    "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", +                    "dev": true +                } +            } +        },          "validate-npm-package-license": {              "version": "3.0.4",              "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -10485,11 +14420,249 @@                  "spdx-expression-parse": "^3.0.0"              }          }, -        "vm-browserify": { -            "version": "1.1.2", -            "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", -            "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", -            "dev": true +        "vite": { +            "version": "4.5.0", +            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", +            "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", +            "dev": true, +            "requires": { +                "esbuild": "^0.18.10", +                "fsevents": "~2.3.2", +                "postcss": "^8.4.27", +                "rollup": "^3.27.1" +            }, +            "dependencies": { +                "@esbuild/android-arm": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", +                    "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/android-arm64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", +                    "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/android-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", +                    "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/darwin-arm64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", +                    "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/darwin-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", +                    "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/freebsd-arm64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", +                    "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/freebsd-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", +                    "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-arm": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", +                    "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-arm64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", +                    "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-ia32": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", +                    "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-loong64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", +                    "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-mips64el": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", +                    "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-ppc64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", +                    "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-riscv64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", +                    "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-s390x": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", +                    "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/linux-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", +                    "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/netbsd-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", +                    "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/openbsd-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", +                    "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/sunos-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", +                    "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/win32-arm64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", +                    "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/win32-ia32": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", +                    "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", +                    "dev": true, +                    "optional": true +                }, +                "@esbuild/win32-x64": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", +                    "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", +                    "dev": true, +                    "optional": true +                }, +                "esbuild": { +                    "version": "0.18.20", +                    "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", +                    "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", +                    "dev": true, +                    "requires": { +                        "@esbuild/android-arm": "0.18.20", +                        "@esbuild/android-arm64": "0.18.20", +                        "@esbuild/android-x64": "0.18.20", +                        "@esbuild/darwin-arm64": "0.18.20", +                        "@esbuild/darwin-x64": "0.18.20", +                        "@esbuild/freebsd-arm64": "0.18.20", +                        "@esbuild/freebsd-x64": "0.18.20", +                        "@esbuild/linux-arm": "0.18.20", +                        "@esbuild/linux-arm64": "0.18.20", +                        "@esbuild/linux-ia32": "0.18.20", +                        "@esbuild/linux-loong64": "0.18.20", +                        "@esbuild/linux-mips64el": "0.18.20", +                        "@esbuild/linux-ppc64": "0.18.20", +                        "@esbuild/linux-riscv64": "0.18.20", +                        "@esbuild/linux-s390x": "0.18.20", +                        "@esbuild/linux-x64": "0.18.20", +                        "@esbuild/netbsd-x64": "0.18.20", +                        "@esbuild/openbsd-x64": "0.18.20", +                        "@esbuild/sunos-x64": "0.18.20", +                        "@esbuild/win32-arm64": "0.18.20", +                        "@esbuild/win32-ia32": "0.18.20", +                        "@esbuild/win32-x64": "0.18.20" +                    } +                } +            } +        }, +        "vite-node": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", +            "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", +            "dev": true, +            "requires": { +                "cac": "^6.7.14", +                "debug": "^4.3.4", +                "mlly": "^1.4.0", +                "pathe": "^1.1.1", +                "picocolors": "^1.0.0", +                "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" +            } +        }, +        "vitest": { +            "version": "0.34.6", +            "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", +            "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", +            "dev": true, +            "requires": { +                "@types/chai": "^4.3.5", +                "@types/chai-subset": "^1.3.3", +                "@types/node": "*", +                "@vitest/expect": "0.34.6", +                "@vitest/runner": "0.34.6", +                "@vitest/snapshot": "0.34.6", +                "@vitest/spy": "0.34.6", +                "@vitest/utils": "0.34.6", +                "acorn": "^8.9.0", +                "acorn-walk": "^8.2.0", +                "cac": "^6.7.14", +                "chai": "^4.3.10", +                "debug": "^4.3.4", +                "local-pkg": "^0.4.3", +                "magic-string": "^0.30.1", +                "pathe": "^1.1.1", +                "picocolors": "^1.0.0", +                "std-env": "^3.3.3", +                "strip-literal": "^1.0.1", +                "tinybench": "^2.5.0", +                "tinypool": "^0.7.0", +                "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", +                "vite-node": "0.34.6", +                "why-is-node-running": "^2.2.2" +            }          },          "w3c-xmlserializer": {              "version": "4.0.0", @@ -10500,6 +14673,17 @@                  "xml-name-validator": "^4.0.0"              }          }, +        "walker": { +            "version": "1.0.8", +            "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", +            "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "makeerror": "1.0.12" +            } +        },          "wanakana": {              "version": "5.2.0",              "resolved": "https://registry.npmjs.org/wanakana/-/wanakana-5.2.0.tgz", @@ -10557,31 +14741,14 @@                  "isexe": "^2.0.0"              }          }, -        "which-boxed-primitive": { -            "version": "1.0.2", -            "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", -            "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", +        "why-is-node-running": { +            "version": "2.2.2", +            "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", +            "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",              "dev": true,              "requires": { -                "is-bigint": "^1.0.1", -                "is-boolean-object": "^1.1.0", -                "is-number-object": "^1.0.4", -                "is-string": "^1.0.5", -                "is-symbol": "^1.0.3" -            } -        }, -        "which-typed-array": { -            "version": "1.1.7", -            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", -            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", -            "dev": true, -            "requires": { -                "available-typed-arrays": "^1.0.5", -                "call-bind": "^1.0.2", -                "es-abstract": "^1.18.5", -                "foreach": "^2.0.5", -                "has-tostringtag": "^1.0.0", -                "is-typed-array": "^1.1.7" +                "siginfo": "^2.0.0", +                "stackback": "0.0.2"              }          },          "wordwrap": { @@ -10695,11 +14862,13 @@              "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",              "dev": true          }, -        "xtend": { -            "version": "4.0.2", -            "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", -            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", -            "dev": true +        "y18n": { +            "version": "5.0.8", +            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", +            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", +            "dev": true, +            "optional": true, +            "peer": true          },          "yallist": {              "version": "4.0.0", @@ -10707,6 +14876,33 @@              "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",              "dev": true          }, +        "yargs": { +            "version": "17.7.2", +            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", +            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", +            "dev": true, +            "optional": true, +            "peer": true, +            "requires": { +                "cliui": "^8.0.1", +                "escalade": "^3.1.1", +                "get-caller-file": "^2.0.5", +                "require-directory": "^2.1.1", +                "string-width": "^4.2.3", +                "y18n": "^5.0.5", +                "yargs-parser": "^21.1.1" +            }, +            "dependencies": { +                "yargs-parser": { +                    "version": "21.1.1", +                    "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", +                    "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", +                    "dev": true, +                    "optional": true, +                    "peer": true +                } +            } +        },          "yargs-parser": {              "version": "20.2.9",              "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", diff --git a/package.json b/package.json index 5224a963..1a6ebf84 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,16 @@      "directories": {          "test": "test"      }, +    "type": "module",      "scripts": { -        "postinstall": "node ./dev/patch-dependencies.js", -        "build": "node ./dev/build.js", -        "test": "npm run test-lint && npm run test-lint-css && npm run test-lint-html && npm run test-code && npm run test-manifest && npm run test-build", -        "test-lint": "npx eslint . && node ./dev/lint/global-declarations.js && node ./dev/lint/html-scripts.js", +        "build": "node ./dev/bin/build.js", +        "build-libs": "node ./dev/bin/build-libs.js", +        "test": "npm run test-lint-js && npm run test-lint-css && npm run test-lint-html && npm run test-code && npm run test-build", +        "test-lint-js": "npx eslint .",          "test-lint-css": "npx stylelint \"ext/**/*.css\" \"test/**/*.css\" \"dev/**/*.css\"",          "test-lint-html": "npx html-validate \"ext/**/*.html\" \"test/**/*.html\" \"dev/**/*.html\"", -        "test-code": "node ./test/test-all.js ./test --skip ./test/test-manifest.js", -        "test-manifest": "node ./test/test-manifest.js", -        "test-build": "node ./dev/build.js --dry-run --all" +        "test-code": "vitest run", +        "test-build": "node ./dev/bin/build.js --dry-run --all"      },      "repository": {          "type": "git", @@ -37,9 +37,9 @@      "devDependencies": {          "@playwright/test": "^1.39.0",          "@types/node": "^20.8.10", +        "@vitest/coverage-v8": "^0.34.6",          "@zip.js/zip.js": "^2.7.30",          "ajv": "^8.12.0", -        "browserify": "^17.0.0",          "css": "^3.0.0",          "dexie": "^3.2.4",          "dexie-export-import": "^4.0.7", @@ -49,6 +49,7 @@          "eslint-plugin-jsdoc": "^46.8.2",          "eslint-plugin-jsonc": "^2.10.0",          "eslint-plugin-no-unsanitized": "^4.0.1", +        "eslint-plugin-vitest": "^0.3.9",          "fake-indexeddb": "^5.0.1",          "handlebars": "^4.7.8",          "html-validate": "^8.7.0", @@ -57,6 +58,7 @@          "parse5": "^7.1.2",          "stylelint": "^15.11.0",          "stylelint-config-recommended": "^13.0.0", +        "vitest": "^0.34.6",          "wanakana": "^5.2.0"      }  } @@ -1,4 +1,4 @@  { pkgs ? import <nixpkgs> { } }:  pkgs.mkShell { -  nativeBuildInputs = [ pkgs.nodejs ]; +  nativeBuildInputs = [ pkgs.nodejs_20 ];  } diff --git a/test/anki-note-builder.test.js b/test/anki-note-builder.test.js new file mode 100644 index 00000000..90bb3cbe --- /dev/null +++ b/test/anki-note-builder.test.js @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2021-2022  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/>. + */ + +import 'fake-indexeddb/auto'; +import fs from 'fs'; +import {fileURLToPath} from 'node:url'; +import path from 'path'; +import url from 'url'; +import {describe, test, vi} from 'vitest'; +import {TranslatorVM} from '../dev/translator-vm.js'; +import {AnkiNoteBuilder} from '../ext/js/data/anki-note-builder.js'; +import {JapaneseUtil} from '../ext/js/language/sandbox/japanese-util.js'; + +vi.stubGlobal('fetch', async (url2) => { +    const extDir = path.join(__dirname, '..', 'ext'); +    let filePath; +    try { +        filePath = url.fileURLToPath(url2); +    } catch (e) { +        filePath = path.resolve(extDir, url2.replace(/^[/\\]/, '')); +    } +    await Promise.resolve(); +    const content = fs.readFileSync(filePath, {encoding: null}); +    return { +        ok: true, +        status: 200, +        statusText: 'OK', +        text: async () => Promise.resolve(content.toString('utf8')), +        json: async () => Promise.resolve(JSON.parse(content.toString('utf8'))) +    }; +}); +vi.mock('../ext/js/templates/template-renderer-proxy.js'); + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function createVM() { +    const dictionaryDirectory = path.join(dirname, 'data', 'dictionaries', 'valid-dictionary1'); +    const vm = new TranslatorVM(); + +    await vm.prepare(dictionaryDirectory, 'Test Dictionary 2'); + +    return vm; +} + +function getFieldMarkers(type) { +    switch (type) { +        case 'terms': +            return [ +                'audio', +                'clipboard-image', +                'clipboard-text', +                'cloze-body', +                'cloze-prefix', +                'cloze-suffix', +                'conjugation', +                'dictionary', +                'document-title', +                'expression', +                'frequencies', +                'furigana', +                'furigana-plain', +                'glossary', +                'glossary-brief', +                'glossary-no-dictionary', +                'part-of-speech', +                'pitch-accents', +                'pitch-accent-graphs', +                'pitch-accent-positions', +                'reading', +                'screenshot', +                'search-query', +                'selection-text', +                'sentence', +                'sentence-furigana', +                'tags', +                'url' +            ]; +        case 'kanji': +            return [ +                'character', +                'clipboard-image', +                'clipboard-text', +                'cloze-body', +                'cloze-prefix', +                'cloze-suffix', +                'dictionary', +                'document-title', +                'glossary', +                'kunyomi', +                'onyomi', +                'screenshot', +                'search-query', +                'selection-text', +                'sentence', +                'sentence-furigana', +                'stroke-count', +                'tags', +                'url' +            ]; +        default: +            return []; +    } +} + +async function getRenderResults(dictionaryEntries, type, mode, template, expect) { +    const markers = getFieldMarkers(type); +    const fields = []; +    for (const marker of markers) { +        fields.push([marker, `{${marker}}`]); +    } + +    const japaneseUtil = new JapaneseUtil(null); +    const clozePrefix = 'cloze-prefix'; +    const clozeSuffix = 'cloze-suffix'; +    const results = []; +    for (const dictionaryEntry of dictionaryEntries) { +        let source = ''; +        switch (dictionaryEntry.type) { +            case 'kanji': +                source = dictionaryEntry.character; +                break; +            case 'term': +                if (dictionaryEntry.headwords.length > 0 && dictionaryEntry.headwords[0].sources.length > 0) { +                    source = dictionaryEntry.headwords[0].sources[0].originalText; +                } +                break; +        } +        const ankiNoteBuilder = new AnkiNoteBuilder({japaneseUtil}); +        const context = { +            url: 'url:', +            sentence: { +                text: `${clozePrefix}${source}${clozeSuffix}`, +                offset: clozePrefix.length +            }, +            documentTitle: 'title', +            query: 'query', +            fullQuery: 'fullQuery' +        }; +        const {note: {fields: noteFields}, errors} = await ankiNoteBuilder.createNote({ +            dictionaryEntry, +            mode: null, +            context, +            template, +            deckName: 'deckName', +            modelName: 'modelName', +            fields, +            tags: ['yomichan'], +            checkForDuplicates: true, +            duplicateScope: 'collection', +            duplicateScopeCheckAllModels: false, +            resultOutputMode: mode, +            glossaryLayoutMode: 'default', +            compactTags: false +        }); +        for (const error of errors) { +            console.error(error); +        } +        expect(errors.length).toStrictEqual(0); +        results.push(noteFields); +    } + +    return results; +} + + +async function main() { +    const vm = await createVM(); + +    const testInputsFilePath = path.join(dirname, 'data', 'translator-test-inputs.json'); +    const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'})); + +    const testResults1FilePath = path.join(dirname, 'data', 'anki-note-builder-test-results.json'); +    const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'})); +    const actualResults1 = []; + +    const template = fs.readFileSync(path.join(dirname, '..', 'ext', 'data/templates/default-anki-field-templates.handlebars'), {encoding: 'utf8'}); + +    describe.concurrent('AnkiNoteBuilder', () => { +        for (let i = 0, ii = tests.length; i < ii; ++i) { +            const t = tests[i]; +            test(`${t.name}`, async ({expect}) => { +                const expected1 = expectedResults1[i]; +                switch (t.func) { +                    case 'findTerms': +                        { +                            const {name, mode, text} = t; +                            const options = vm.buildOptions(optionsPresets, t.options); +                            const {dictionaryEntries} = structuredClone(await vm.translator.findTerms(mode, text, options)); +                            const results = mode !== 'simple' ? structuredClone(await getRenderResults(dictionaryEntries, 'terms', mode, template, expect)) : null; +                            actualResults1.push({name, results}); +                            expect(results).toStrictEqual(expected1.results); +                        } +                        break; +                    case 'findKanji': +                        { +                            const {name, text} = t; +                            const options = vm.buildOptions(optionsPresets, t.options); +                            const dictionaryEntries = structuredClone(await vm.translator.findKanji(text, options)); +                            const results = structuredClone(await getRenderResults(dictionaryEntries, 'kanji', null, template, expect)); +                            actualResults1.push({name, results}); +                            expect(results).toStrictEqual(expected1.results); +                        } +                        break; +                } +            }); +        } +    }); +} +await main(); diff --git a/test/cache-map.test.js b/test/cache-map.test.js new file mode 100644 index 00000000..9d10a719 --- /dev/null +++ b/test/cache-map.test.js @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {CacheMap} from '../ext/js/general/cache-map.js'; + +function testConstructor() { +    test('constructor', () => { +        const data = [ +            [false, () => new CacheMap(0)], +            [false, () => new CacheMap(1)], +            [false, () => new CacheMap(Number.MAX_VALUE)], +            [true,  () => new CacheMap(-1)], +            [true,  () => new CacheMap(1.5)], +            [true,  () => new CacheMap(Number.NaN)], +            [true,  () => new CacheMap(Number.POSITIVE_INFINITY)], +            [true,  () => new CacheMap('a')] +        ]; + +        for (const [throws, create] of data) { +            if (throws) { +                expect(create).toThrowError(); +            } else { +                expect(create).not.toThrowError(); +            } +        } +    }); +} + +function testApi() { +    test('api', () => { +        const data = [ +            { +                maxSize: 1, +                expectedSize: 0, +                calls: [] +            }, +            { +                maxSize: 10, +                expectedSize: 1, +                calls: [ +                    {func: 'get', args: ['a1-b-c'],     returnValue: void 0}, +                    {func: 'has', args: ['a1-b-c'],     returnValue: false}, +                    {func: 'set', args: ['a1-b-c', 32], returnValue: void 0}, +                    {func: 'get', args: ['a1-b-c'],     returnValue: 32}, +                    {func: 'has', args: ['a1-b-c'],     returnValue: true} +                ] +            }, +            { +                maxSize: 10, +                expectedSize: 2, +                calls: [ +                    {func: 'set', args: ['a1-b-c', 32], returnValue: void 0}, +                    {func: 'get', args: ['a1-b-c'],     returnValue: 32}, +                    {func: 'set', args: ['a1-b-c', 64], returnValue: void 0}, +                    {func: 'get', args: ['a1-b-c'],     returnValue: 64}, +                    {func: 'set', args: ['a2-b-c', 96], returnValue: void 0}, +                    {func: 'get', args: ['a2-b-c'],     returnValue: 96} +                ] +            }, +            { +                maxSize: 2, +                expectedSize: 2, +                calls: [ +                    {func: 'has', args: ['a1-b-c'],    returnValue: false}, +                    {func: 'has', args: ['a2-b-c'],    returnValue: false}, +                    {func: 'has', args: ['a3-b-c'],    returnValue: false}, +                    {func: 'set', args: ['a1-b-c', 1], returnValue: void 0}, +                    {func: 'has', args: ['a1-b-c'],    returnValue: true}, +                    {func: 'has', args: ['a2-b-c'],    returnValue: false}, +                    {func: 'has', args: ['a3-b-c'],    returnValue: false}, +                    {func: 'set', args: ['a2-b-c', 2], returnValue: void 0}, +                    {func: 'has', args: ['a1-b-c'],    returnValue: true}, +                    {func: 'has', args: ['a2-b-c'],    returnValue: true}, +                    {func: 'has', args: ['a3-b-c'],    returnValue: false}, +                    {func: 'set', args: ['a3-b-c', 3], returnValue: void 0}, +                    {func: 'has', args: ['a1-b-c'],    returnValue: false}, +                    {func: 'has', args: ['a2-b-c'],    returnValue: true}, +                    {func: 'has', args: ['a3-b-c'],    returnValue: true} +                ] +            } +        ]; + +        for (const {maxSize, expectedSize, calls} of data) { +            const cache = new CacheMap(maxSize); +            expect(cache.maxSize).toStrictEqual(maxSize); +            for (const call of calls) { +                const {func, args} = call; +                let returnValue; +                switch (func) { +                    case 'get': returnValue = cache.get(...args); break; +                    case 'set': returnValue = cache.set(...args); break; +                    case 'has': returnValue = cache.has(...args); break; +                    case 'clear': returnValue = cache.clear(...args); break; +                } +                if (Object.prototype.hasOwnProperty.call(call, 'returnValue')) { +                    const {returnValue: expectedReturnValue} = call; +                    expect(returnValue).toStrictEqual(expectedReturnValue); +                } +            } +            expect(cache.size).toStrictEqual(expectedSize); +        } +    }); +} + + +function main() { +    testConstructor(); +    testApi(); +} + + +main(); diff --git a/test/core.test.js b/test/core.test.js new file mode 100644 index 00000000..203460f4 --- /dev/null +++ b/test/core.test.js @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {describe, expect, test} from 'vitest'; +import {DynamicProperty, deepEqual} from '../ext/js/core.js'; + +function testDynamicProperty() { +    test('DynamicProperty', () => { +        const data = [ +            { +                initialValue: 0, +                operations: [ +                    { +                        operation: null, +                        expectedDefaultValue: 0, +                        expectedValue: 0, +                        expectedOverrideCount: 0, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'set.defaultValue', +                        args: [1], +                        expectedDefaultValue: 1, +                        expectedValue: 1, +                        expectedOverrideCount: 0, +                        expeectedEventOccurred: true +                    }, +                    { +                        operation: 'set.defaultValue', +                        args: [1], +                        expectedDefaultValue: 1, +                        expectedValue: 1, +                        expectedOverrideCount: 0, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'set.defaultValue', +                        args: [0], +                        expectedDefaultValue: 0, +                        expectedValue: 0, +                        expectedOverrideCount: 0, +                        expeectedEventOccurred: true +                    }, +                    { +                        operation: 'setOverride', +                        args: [8], +                        expectedDefaultValue: 0, +                        expectedValue: 8, +                        expectedOverrideCount: 1, +                        expeectedEventOccurred: true +                    }, +                    { +                        operation: 'setOverride', +                        args: [16], +                        expectedDefaultValue: 0, +                        expectedValue: 8, +                        expectedOverrideCount: 2, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'setOverride', +                        args: [32, 1], +                        expectedDefaultValue: 0, +                        expectedValue: 32, +                        expectedOverrideCount: 3, +                        expeectedEventOccurred: true +                    }, +                    { +                        operation: 'setOverride', +                        args: [64, -1], +                        expectedDefaultValue: 0, +                        expectedValue: 32, +                        expectedOverrideCount: 4, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'clearOverride', +                        args: [-4], +                        expectedDefaultValue: 0, +                        expectedValue: 32, +                        expectedOverrideCount: 3, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'clearOverride', +                        args: [-3], +                        expectedDefaultValue: 0, +                        expectedValue: 32, +                        expectedOverrideCount: 2, +                        expeectedEventOccurred: false +                    }, +                    { +                        operation: 'clearOverride', +                        args: [-2], +                        expectedDefaultValue: 0, +                        expectedValue: 64, +                        expectedOverrideCount: 1, +                        expeectedEventOccurred: true +                    }, +                    { +                        operation: 'clearOverride', +                        args: [-1], +                        expectedDefaultValue: 0, +                        expectedValue: 0, +                        expectedOverrideCount: 0, +                        expeectedEventOccurred: true +                    } +                ] +            } +        ]; + +        for (const {initialValue, operations} of data) { +            const property = new DynamicProperty(initialValue); +            const overrideTokens = []; +            let eventOccurred = false; +            const onChange = () => { eventOccurred = true; }; +            property.on('change', onChange); +            for (const {operation, args, expectedDefaultValue, expectedValue, expectedOverrideCount, expeectedEventOccurred} of operations) { +                eventOccurred = false; +                switch (operation) { +                    case 'set.defaultValue': property.defaultValue = args[0]; break; +                    case 'setOverride': overrideTokens.push(property.setOverride(...args)); break; +                    case 'clearOverride': property.clearOverride(overrideTokens[overrideTokens.length + args[0]]); break; +                } +                expect(eventOccurred).toStrictEqual(expeectedEventOccurred); +                expect(property.defaultValue).toStrictEqual(expectedDefaultValue); +                expect(property.value).toStrictEqual(expectedValue); +                expect(property.overrideCount).toStrictEqual(expectedOverrideCount); +            } +            property.off('change', onChange); +        } +    }); +} + +function testDeepEqual() { +    describe('deepEqual', () => { +        const data = [ +        // Simple tests +            { +                value1: 0, +                value2: 0, +                expected: true +            }, +            { +                value1: null, +                value2: null, +                expected: true +            }, +            { +                value1: 'test', +                value2: 'test', +                expected: true +            }, +            { +                value1: true, +                value2: true, +                expected: true +            }, +            { +                value1: 0, +                value2: 1, +                expected: false +            }, +            { +                value1: null, +                value2: false, +                expected: false +            }, +            { +                value1: 'test1', +                value2: 'test2', +                expected: false +            }, +            { +                value1: true, +                value2: false, +                expected: false +            }, + +            // Simple object tests +            { +                value1: {}, +                value2: {}, +                expected: true +            }, +            { +                value1: {}, +                value2: [], +                expected: false +            }, +            { +                value1: [], +                value2: [], +                expected: true +            }, +            { +                value1: {}, +                value2: null, +                expected: false +            }, + +            // Complex object tests +            { +                value1: [1], +                value2: [], +                expected: false +            }, +            { +                value1: [1], +                value2: [1], +                expected: true +            }, +            { +                value1: [1], +                value2: [2], +                expected: false +            }, + +            { +                value1: {}, +                value2: {test: 1}, +                expected: false +            }, +            { +                value1: {test: 1}, +                value2: {test: 1}, +                expected: true +            }, +            { +                value1: {test: 1}, +                value2: {test: {test2: false}}, +                expected: false +            }, +            { +                value1: {test: {test2: true}}, +                value2: {test: {test2: false}}, +                expected: false +            }, +            { +                value1: {test: {test2: [true]}}, +                value2: {test: {test2: [true]}}, +                expected: true +            }, + +            // Recursive +            { +                value1: (() => { const x = {}; x.x = x; return x; })(), +                value2: (() => { const x = {}; x.x = x; return x; })(), +                expected: false +            } +        ]; + +        let index = 0; +        for (const {value1, value2, expected} of data) { +            test(`${index}`, () => { +                const actual1 = deepEqual(value1, value2); +                expect(actual1).toStrictEqual(expected); + +                const actual2 = deepEqual(value2, value1); +                expect(actual2).toStrictEqual(expected); +            }); +            ++index; +        } +    }); +} + + +function main() { +    testDynamicProperty(); +    testDeepEqual(); +} + +main(); diff --git a/test/css-json.test.js b/test/css-json.test.js new file mode 100644 index 00000000..0aaf7d10 --- /dev/null +++ b/test/css-json.test.js @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2021-2022  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/>. + */ + +import fs from 'fs'; +import {expect, test} from 'vitest'; +import {formatRulesJson, generateRules, getTargets} from '../dev/generate-css-json'; + +function main() { +    test('css-json', () => { +        for (const {cssFile, overridesCssFile, outputPath} of getTargets()) { +            const actual = fs.readFileSync(outputPath, {encoding: 'utf8'}); +            const expected = formatRulesJson(generateRules(cssFile, overridesCssFile)); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +main(); diff --git a/test/database.test.js b/test/database.test.js new file mode 100644 index 00000000..b53d0e65 --- /dev/null +++ b/test/database.test.js @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {IDBFactory, IDBKeyRange} from 'fake-indexeddb'; +import path from 'path'; +import {beforeEach, describe, expect, test, vi} from 'vitest'; +import {createDictionaryArchive} from '../dev/util.js'; +import {DictionaryDatabase} from '../ext/js/language/dictionary-database.js'; +import {DictionaryImporterMediaLoader} from '../ext/js/language/dictionary-importer-media-loader.js'; +import {DictionaryImporter} from '../ext/js/language/dictionary-importer.js'; + +vi.stubGlobal('IDBKeyRange', IDBKeyRange); + +vi.mock('../ext/js/language/dictionary-importer-media-loader.js'); + +function createTestDictionaryArchive(dictionary, dictionaryName) { +    const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', dictionary); +    return createDictionaryArchive(dictionaryDirectory, dictionaryName); +} + + +function createDictionaryImporter(onProgress) { +    const dictionaryImporterMediaLoader = new DictionaryImporterMediaLoader(); +    return new DictionaryImporter(dictionaryImporterMediaLoader, (...args) => { +        const {stepIndex, stepCount, index, count} = args[0]; +        expect(stepIndex < stepCount).toBe(true); +        expect(index <= count).toBe(true); +        if (typeof onProgress === 'function') { +            onProgress(...args); +        } +    }); +} + + +function countDictionaryDatabaseEntriesWithTerm(dictionaryDatabaseEntries, term) { +    return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.term === term ? 1 : 0)), 0); +} + +function countDictionaryDatabaseEntriesWithReading(dictionaryDatabaseEntries, reading) { +    return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.reading === reading ? 1 : 0)), 0); +} + +function countMetasWithMode(metas, mode) { +    return metas.reduce((i, v) => (i + (v.mode === mode ? 1 : 0)), 0); +} + +function countKanjiWithCharacter(kanji, character) { +    return kanji.reduce((i, v) => (i + (v.character === character ? 1 : 0)), 0); +} + + + +async function testDatabase1() { +    test('Database1', async () => {    // Load dictionary data +        const testDictionary = createTestDictionaryArchive('valid-dictionary1'); +        const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); +        const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string')); + +        const title = testDictionaryIndex.title; +        const titles = new Map([ +            [title, {priority: 0, allowSecondarySearches: false}] +        ]); + +        // Setup iteration data +        const iterations = [ +            { +                cleanup: async () => { +                // Test purge +                    await dictionaryDatabase.purge(); +                    await testDatabaseEmpty1(dictionaryDatabase); +                } +            }, +            { +                cleanup: async () => { +                // Test deleteDictionary +                    let progressEvent = false; +                    await dictionaryDatabase.deleteDictionary( +                        title, +                        1000, +                        () => { +                            progressEvent = true; +                        } +                    ); +                    expect(progressEvent).toBe(true); + +                    await testDatabaseEmpty1(dictionaryDatabase); +                } +            }, +            { +                cleanup: async () => {} +            } +        ]; + +        // Setup database +        const dictionaryDatabase = new DictionaryDatabase(); +        await dictionaryDatabase.prepare(); + +        for (const {cleanup} of iterations) { +            const expectedSummary = { +                title, +                revision: 'test', +                sequenced: true, +                version: 3, +                importDate: 0, +                prefixWildcardsSupported: true, +                counts: { +                    kanji: {total: 2}, +                    kanjiMeta: {total: 6, freq: 6}, +                    media: {total: 4}, +                    tagMeta: {total: 15}, +                    termMeta: {total: 38, freq: 31, pitch: 7}, +                    terms: {total: 21} +                } +            }; + +            // Import data +            let progressEvent = false; +            const dictionaryImporter = createDictionaryImporter(() => { progressEvent = true; }); +            const {result, errors} = await dictionaryImporter.importDictionary( +                dictionaryDatabase, +                testDictionarySource, +                {prefixWildcardsSupported: true} +            ); +            expectedSummary.importDate = result.importDate; +            expect(errors).toStrictEqual([]); +            expect(result).toStrictEqual(expectedSummary); +            expect(progressEvent).toBe(true); + +            // Get info summary +            const info = await dictionaryDatabase.getDictionaryInfo(); +            expect(info).toStrictEqual([expectedSummary]); + +            // Get counts +            const counts = await dictionaryDatabase.getDictionaryCounts( +                info.map((v) => v.title), +                true +            ); +            expect(counts).toStrictEqual({ +                counts: [{kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4}], +                total: {kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4} +            }); + +            // Test find* functions +            await testFindTermsBulkTest1(dictionaryDatabase, titles); +            await testTindTermsExactBulk1(dictionaryDatabase, titles); +            await testFindTermsBySequenceBulk1(dictionaryDatabase, title); +            await testFindTermMetaBulk1(dictionaryDatabase, titles); +            await testFindKanjiBulk1(dictionaryDatabase, titles); +            await testFindKanjiMetaBulk1(dictionaryDatabase, titles); +            await testFindTagForTitle1(dictionaryDatabase, title); + +            // Cleanup +            await cleanup(); +        } + +        await dictionaryDatabase.close(); +    }); +} + +async function testDatabaseEmpty1(database) { +    test('DatabaseEmpty1', async () => { +        const info = await database.getDictionaryInfo(); +        expect(info).toStrictEqual([]); + +        const counts = await database.getDictionaryCounts([], true); +        expect(counts).toStrictEqual({ +            counts: [], +            total: {kanji: 0, kanjiMeta: 0, terms: 0, termMeta: 0, tagMeta: 0, media: 0} +        }); +    }); +} + +async function testFindTermsBulkTest1(database, titles) { +    test('FindTermsBulkTest1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        matchType: null, +                        termList: ['打', '打つ', '打ち込む'] +                    }, +                    { +                        matchType: null, +                        termList: ['だ', 'ダース', 'うつ', 'ぶつ', 'うちこむ', 'ぶちこむ'] +                    }, +                    { +                        matchType: 'prefix', +                        termList: ['打'] +                    } +                ], +                expectedResults: { +                    total: 10, +                    terms: [ +                        ['打', 2], +                        ['打つ', 4], +                        ['打ち込む', 4] +                    ], +                    readings: [ +                        ['だ', 1], +                        ['ダース', 1], +                        ['うつ', 2], +                        ['ぶつ', 2], +                        ['うちこむ', 2], +                        ['ぶちこむ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        matchType: null, +                        termList: ['込む'] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            }, +            { +                inputs: [ +                    { +                        matchType: 'suffix', +                        termList: ['込む'] +                    } +                ], +                expectedResults: { +                    total: 4, +                    terms: [ +                        ['打ち込む', 4] +                    ], +                    readings: [ +                        ['うちこむ', 2], +                        ['ぶちこむ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        matchType: null, +                        termList: [] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {termList, matchType} of inputs) { +                const results = await database.findTermsBulk(termList, titles, matchType); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [term, count] of expectedResults.terms) { +                    expect(countDictionaryDatabaseEntriesWithTerm(results, term)).toStrictEqual(count); +                } +                for (const [reading, count] of expectedResults.readings) { +                    expect(countDictionaryDatabaseEntriesWithReading(results, reading)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testTindTermsExactBulk1(database, titles) { +    test('TindTermsExactBulk1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        termList: [ +                            {term: '打', reading: 'だ'}, +                            {term: '打つ', reading: 'うつ'}, +                            {term: '打ち込む', reading: 'うちこむ'} +                        ] +                    } +                ], +                expectedResults: { +                    total: 5, +                    terms: [ +                        ['打', 1], +                        ['打つ', 2], +                        ['打ち込む', 2] +                    ], +                    readings: [ +                        ['だ', 1], +                        ['うつ', 2], +                        ['うちこむ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        termList: [ +                            {term: '打', reading: 'だ?'}, +                            {term: '打つ', reading: 'うつ?'}, +                            {term: '打ち込む', reading: 'うちこむ?'} +                        ] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            }, +            { +                inputs: [ +                    { +                        termList: [ +                            {term: '打つ', reading: 'うつ'}, +                            {term: '打つ', reading: 'ぶつ'} +                        ] +                    } +                ], +                expectedResults: { +                    total: 4, +                    terms: [ +                        ['打つ', 4] +                    ], +                    readings: [ +                        ['うつ', 2], +                        ['ぶつ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        termList: [ +                            {term: '打つ', reading: 'うちこむ'} +                        ] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            }, +            { +                inputs: [ +                    { +                        termList: [] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {termList} of inputs) { +                const results = await database.findTermsExactBulk(termList, titles); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [term, count] of expectedResults.terms) { +                    expect(countDictionaryDatabaseEntriesWithTerm(results, term)).toStrictEqual(count); +                } +                for (const [reading, count] of expectedResults.readings) { +                    expect(countDictionaryDatabaseEntriesWithReading(results, reading)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testFindTermsBySequenceBulk1(database, mainDictionary) { +    test('FindTermsBySequenceBulk1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        sequenceList: [1, 2, 3, 4, 5] +                    } +                ], +                expectedResults: { +                    total: 11, +                    terms: [ +                        ['打', 2], +                        ['打つ', 4], +                        ['打ち込む', 4], +                        ['画像', 1] +                    ], +                    readings: [ +                        ['だ', 1], +                        ['ダース', 1], +                        ['うつ', 2], +                        ['ぶつ', 2], +                        ['うちこむ', 2], +                        ['ぶちこむ', 2], +                        ['がぞう', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [1] +                    } +                ], +                expectedResults: { +                    total: 1, +                    terms: [ +                        ['打', 1] +                    ], +                    readings: [ +                        ['だ', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [2] +                    } +                ], +                expectedResults: { +                    total: 1, +                    terms: [ +                        ['打', 1] +                    ], +                    readings: [ +                        ['ダース', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [3] +                    } +                ], +                expectedResults: { +                    total: 4, +                    terms: [ +                        ['打つ', 4] +                    ], +                    readings: [ +                        ['うつ', 2], +                        ['ぶつ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [4] +                    } +                ], +                expectedResults: { +                    total: 4, +                    terms: [ +                        ['打ち込む', 4] +                    ], +                    readings: [ +                        ['うちこむ', 2], +                        ['ぶちこむ', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [5] +                    } +                ], +                expectedResults: { +                    total: 1, +                    terms: [ +                        ['画像', 1] +                    ], +                    readings: [ +                        ['がぞう', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [-1] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            }, +            { +                inputs: [ +                    { +                        sequenceList: [] +                    } +                ], +                expectedResults: { +                    total: 0, +                    terms: [], +                    readings: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {sequenceList} of inputs) { +                const results = await database.findTermsBySequenceBulk(sequenceList.map((query) => ({query, dictionary: mainDictionary}))); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [term, count] of expectedResults.terms) { +                    expect(countDictionaryDatabaseEntriesWithTerm(results, term)).toStrictEqual(count); +                } +                for (const [reading, count] of expectedResults.readings) { +                    expect(countDictionaryDatabaseEntriesWithReading(results, reading)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testFindTermMetaBulk1(database, titles) { +    test('FindTermMetaBulk1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        termList: ['打'] +                    } +                ], +                expectedResults: { +                    total: 11, +                    modes: [ +                        ['freq', 11] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        termList: ['打つ'] +                    } +                ], +                expectedResults: { +                    total: 10, +                    modes: [ +                        ['freq', 10] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        termList: ['打ち込む'] +                    } +                ], +                expectedResults: { +                    total: 12, +                    modes: [ +                        ['freq', 10], +                        ['pitch', 2] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        termList: ['?'] +                    } +                ], +                expectedResults: { +                    total: 0, +                    modes: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {termList} of inputs) { +                const results = await database.findTermMetaBulk(termList, titles); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [mode, count] of expectedResults.modes) { +                    expect(countMetasWithMode(results, mode)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testFindKanjiBulk1(database, titles) { +    test('FindKanjiBulk1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        kanjiList: ['打'] +                    } +                ], +                expectedResults: { +                    total: 1, +                    kanji: [ +                        ['打', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        kanjiList: ['込'] +                    } +                ], +                expectedResults: { +                    total: 1, +                    kanji: [ +                        ['込', 1] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        kanjiList: ['?'] +                    } +                ], +                expectedResults: { +                    total: 0, +                    kanji: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {kanjiList} of inputs) { +                const results = await database.findKanjiBulk(kanjiList, titles); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [kanji, count] of expectedResults.kanji) { +                    expect(countKanjiWithCharacter(results, kanji)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testFindKanjiMetaBulk1(database, titles) { +    test('FindKanjiMetaBulk1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        kanjiList: ['打'] +                    } +                ], +                expectedResults: { +                    total: 3, +                    modes: [ +                        ['freq', 3] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        kanjiList: ['込'] +                    } +                ], +                expectedResults: { +                    total: 3, +                    modes: [ +                        ['freq', 3] +                    ] +                } +            }, +            { +                inputs: [ +                    { +                        kanjiList: ['?'] +                    } +                ], +                expectedResults: { +                    total: 0, +                    modes: [] +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {kanjiList} of inputs) { +                const results = await database.findKanjiMetaBulk(kanjiList, titles); +                expect(results.length).toStrictEqual(expectedResults.total); +                for (const [mode, count] of expectedResults.modes) { +                    expect(countMetasWithMode(results, mode)).toStrictEqual(count); +                } +            } +        } +    }); +} + +async function testFindTagForTitle1(database, title) { +    test('FindTagForTitle1', async () => { +        const data = [ +            { +                inputs: [ +                    { +                        name: 'E1' +                    } +                ], +                expectedResults: { +                    value: {category: 'default', dictionary: title, name: 'E1', notes: 'example tag 1', order: 0, score: 0} +                } +            }, +            { +                inputs: [ +                    { +                        name: 'K1' +                    } +                ], +                expectedResults: { +                    value: {category: 'default', dictionary: title, name: 'K1', notes: 'example kanji tag 1', order: 0, score: 0} +                } +            }, +            { +                inputs: [ +                    { +                        name: 'kstat1' +                    } +                ], +                expectedResults: { +                    value: {category: 'class', dictionary: title, name: 'kstat1', notes: 'kanji stat 1', order: 0, score: 0} +                } +            }, +            { +                inputs: [ +                    { +                        name: 'invalid' +                    } +                ], +                expectedResults: { +                    value: null +                } +            } +        ]; + +        for (const {inputs, expectedResults} of data) { +            for (const {name} of inputs) { +                const result = await database.findTagForTitle(name, title); +                expect(result).toStrictEqual(expectedResults.value); +            } +        } +    }); +} + + +async function testDatabase2() { +    test('Database2', async () => {    // Load dictionary data +        const testDictionary = createTestDictionaryArchive('valid-dictionary1'); +        const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); +        const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string')); + +        const title = testDictionaryIndex.title; +        const titles = new Map([ +            [title, {priority: 0, allowSecondarySearches: false}] +        ]); + +        // Setup database +        const dictionaryDatabase = new DictionaryDatabase(); + +        // Database not open +        await expect(dictionaryDatabase.deleteDictionary(title, 1000)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTermsBulk(['?'], titles, null)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTermsExactBulk([{term: '?', reading: '?'}], titles)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTermsBySequenceBulk([{query: 1, dictionary: title}])).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTermMetaBulk(['?'], titles)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTermMetaBulk(['?'], titles)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findKanjiBulk(['?'], titles)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findKanjiMetaBulk(['?'], titles)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.findTagForTitle('tag', title)).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.getDictionaryInfo()).rejects.toThrow('Database not open'); +        await expect(dictionaryDatabase.getDictionaryCounts(titles, true)).rejects.toThrow('Database not open'); +        await expect(createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {})).rejects.toThrow('Database is not ready'); + +        await dictionaryDatabase.prepare(); + +        // already prepared +        await expect(dictionaryDatabase.prepare()).rejects.toThrow('Database already open'); + +        await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {}); + +        // dictionary already imported +        await expect(createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {})).rejects.toThrow('Dictionary is already imported'); + +        await dictionaryDatabase.close(); +    }); +} + + +async function testDatabase3() { +    const invalidDictionaries = [ +        'invalid-dictionary1', +        'invalid-dictionary2', +        'invalid-dictionary3', +        'invalid-dictionary4', +        'invalid-dictionary5', +        'invalid-dictionary6' +    ]; + + +    describe('Database3', () => { +        for (const invalidDictionary of invalidDictionaries) { +            test(`${invalidDictionary}`, async () => { +                // Setup database +                const dictionaryDatabase = new DictionaryDatabase(); +                await dictionaryDatabase.prepare(); + +                const testDictionary = createTestDictionaryArchive(invalidDictionary); +                const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); + +                await expect(createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {})).rejects.toThrow('Dictionary has invalid data'); +                await dictionaryDatabase.close(); +            }); +        } +    }); +} + + +async function main() { +    beforeEach(async () => { +        globalThis.indexedDB = new IDBFactory(); +    }); +    await testDatabase1(); +    await testDatabase2(); +    await testDatabase3(); +} + +await main(); diff --git a/test/test-deinflector.js b/test/deinflector.test.js index a20cfc95..edb85833 100644 --- a/test/test-deinflector.js +++ b/test/deinflector.test.js @@ -16,14 +16,12 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); +import fs from 'fs'; +import path from 'path'; +import {describe, expect, test} from 'vitest'; +import {Deinflector} from '../ext/js/language/deinflector.js'; - -function hasTermReasons(Deinflector, deinflector, source, expectedTerm, expectedRule, expectedReasons) { +function hasTermReasons(deinflector, source, expectedTerm, expectedRule, expectedReasons) {      for (const {term, reasons, rules} of deinflector.deinflect(source, source)) {          if (term !== expectedTerm) { continue; }          if (typeof expectedRule !== 'undefined') { @@ -917,30 +915,27 @@ function testDeinflections() {          }      ]; -    const vm = new VM(); -    vm.execute(['js/language/deinflector.js']); -    const [Deinflector] = vm.get(['Deinflector']); -      const deinflectionReasons = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'ext', 'data/deinflect.json')));      const deinflector = new Deinflector(deinflectionReasons); -    for (const {valid, tests} of data) { -        for (const {source, term, rule, reasons} of tests) { -            const {has, reasons: actualReasons} = hasTermReasons(Deinflector, deinflector, source, term, rule, reasons); -            let message = `${source} ${valid ? 'does not have' : 'has'} term candidate ${JSON.stringify(term)}`; -            if (typeof rule !== 'undefined') { -                message += ` with rule ${JSON.stringify(rule)}`; -            } -            if (typeof reasons !== 'undefined') { -                message += (typeof rule !== 'undefined' ? ' and' : ' with'); -                message += ` reasons ${JSON.stringify(reasons)}`; -            } -            if (actualReasons !== null) { -                message += ` (actual reasons: ${JSON.stringify(actualReasons)})`; +    describe('deinflections', () => { +        for (const {valid, tests} of data) { +            for (const {source, term, rule, reasons} of tests) { +                const {has} = hasTermReasons(deinflector, source, term, rule, reasons); +                let message = `${source} ${valid ? 'has' : 'does not have'} term candidate ${JSON.stringify(term)}`; +                if (typeof rule !== 'undefined') { +                    message += ` with rule ${JSON.stringify(rule)}`; +                } +                if (typeof reasons !== 'undefined') { +                    message += (typeof rule !== 'undefined' ? ' and' : ' with'); +                    message += ` reasons ${JSON.stringify(reasons)}`; +                } +                test(`${message}`, () => { +                    expect(has).toStrictEqual(valid); +                });              } -            assert.strictEqual(has, valid, message);          } -    } +    });  } @@ -949,4 +944,4 @@ function main() {  } -if (require.main === module) { testMain(main); } +main(); diff --git a/test/test-dictionary.js b/test/dictionary.test.js index d4390e19..8f160bc1 100644 --- a/test/test-dictionary.js +++ b/test/dictionary.test.js @@ -16,13 +16,16 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const path = require('path'); -const {createDictionaryArchive, testMain} = require('../dev/util'); -const dictionaryValidate = require('../dev/dictionary-validate'); +import {fileURLToPath} from 'node:url'; +import path from 'path'; +import {expect, test} from 'vitest'; +import * as dictionaryValidate from '../dev/dictionary-validate.js'; +import {createDictionaryArchive} from '../dev/util.js'; +const dirname = path.dirname(fileURLToPath(import.meta.url));  function createTestDictionaryArchive(dictionary, dictionaryName) { -    const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', dictionary); +    const dictionaryDirectory = path.join(dirname, 'data', 'dictionaries', dictionary);      return createDictionaryArchive(dictionaryDirectory, dictionaryName);  } @@ -41,26 +44,16 @@ async function main() {      const schemas = dictionaryValidate.getSchemas();      for (const {name, valid} of dictionaries) { -        const archive = createTestDictionaryArchive(name); +        test(`${name} is ${valid ? 'valid' : 'invalid'}`, async () => { +            const archive = createTestDictionaryArchive(name); -        let error = null; -        try { -            await dictionaryValidate.validateDictionary(null, archive, schemas); -        } catch (e) { -            error = e; -        } - -        if (valid) { -            if (error !== null) { -                throw error; -            } -        } else { -            if (error === null) { -                throw new Error(`Expected dictionary ${name} to be invalid`); +            if (valid) { +                await expect(dictionaryValidate.validateDictionary(null, archive, schemas)).resolves.not.toThrow(); +            } else { +                await expect(dictionaryValidate.validateDictionary(null, archive, schemas)).rejects.toThrow();              } -        } +        });      }  } - -if (require.main === module) { testMain(main); } +await main(); diff --git a/test/document-util.test.js b/test/document-util.test.js new file mode 100644 index 00000000..f2552f78 --- /dev/null +++ b/test/document-util.test.js @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import fs from 'fs'; +import {JSDOM} from 'jsdom'; +import {fileURLToPath} from 'node:url'; +import path from 'path'; +import {expect, test} from 'vitest'; +import {DocumentUtil} from '../ext/js/dom/document-util.js'; +import {DOMTextScanner} from '../ext/js/dom/dom-text-scanner.js'; +import {TextSourceElement} from '../ext/js/dom/text-source-element.js'; +import {TextSourceRange} from '../ext/js/dom/text-source-range.js'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +// DOMRect class definition +class DOMRect { +    constructor(x, y, width, height) { +        this._x = x; +        this._y = y; +        this._width = width; +        this._height = height; +    } + +    get x() { return this._x; } +    get y() { return this._y; } +    get width() { return this._width; } +    get height() { return this._height; } +    get left() { return this._x + Math.min(0, this._width); } +    get right() { return this._x + Math.max(0, this._width); } +    get top() { return this._y + Math.min(0, this._height); } +    get bottom() { return this._y + Math.max(0, this._height); } +} + + +function createJSDOM(fileName) { +    const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); +    const dom = new JSDOM(domSource); +    const document = dom.window.document; +    const window = dom.window; + +    // Define innerText setter as an alias for textContent setter +    Object.defineProperty(window.HTMLDivElement.prototype, 'innerText', { +        set(value) { this.textContent = value; } +    }); + +    // Placeholder for feature detection +    document.caretRangeFromPoint = () => null; + +    return dom; +} + +function querySelectorChildOrSelf(element, selector) { +    return selector ? element.querySelector(selector) : element; +} + +function getChildTextNodeOrSelf(dom, node) { +    if (node === null) { return null; } +    const Node = dom.window.Node; +    const childNode = node.firstChild; +    return (childNode !== null && childNode.nodeType === Node.TEXT_NODE ? childNode : node); +} + +function getPrototypeOfOrNull(value) { +    try { +        return Object.getPrototypeOf(value); +    } catch (e) { +        return null; +    } +} + +function findImposterElement(document) { +    // Finds the imposter element based on it's z-index style +    return document.querySelector('div[style*="2147483646"]>*'); +} + + +async function testDocument1() { +    const dom = createJSDOM(path.join(dirname, 'data', 'html', 'test-document1.html')); +    const window = dom.window; + +    try { +        await testDocumentTextScanningFunctions(dom); +        await testTextSourceRangeSeekFunctions(dom); +    } finally { +        window.close(); +    } +} + +async function testDocumentTextScanningFunctions(dom) { +    const document = dom.window.document; + +    test('DocumentTextScanningFunctions', () => { +        for (const testElement of document.querySelectorAll('.test[data-test-type=scan]')) { +        // Get test parameters +            let { +                elementFromPointSelector, +                caretRangeFromPointSelector, +                startNodeSelector, +                startOffset, +                endNodeSelector, +                endOffset, +                resultType, +                sentenceScanExtent, +                sentence, +                hasImposter, +                terminateAtNewlines +            } = testElement.dataset; + +            const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector); +            const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector); +            const startNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, startNodeSelector)); +            const endNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, endNodeSelector)); + +            startOffset = parseInt(startOffset, 10); +            endOffset = parseInt(endOffset, 10); +            sentenceScanExtent = parseInt(sentenceScanExtent, 10); +            terminateAtNewlines = (terminateAtNewlines !== 'false'); + +            expect(elementFromPointValue).not.toStrictEqual(null); +            expect(caretRangeFromPointValue).not.toStrictEqual(null); +            expect(startNode).not.toStrictEqual(null); +            expect(endNode).not.toStrictEqual(null); + +            // Setup functions +            document.elementFromPoint = () => elementFromPointValue; + +            document.caretRangeFromPoint = (x, y) => { +                const imposter = getChildTextNodeOrSelf(dom, findImposterElement(document)); +                expect(!!imposter).toStrictEqual(hasImposter === 'true'); + +                const range = document.createRange(); +                range.setStart(imposter ? imposter : startNode, startOffset); +                range.setEnd(imposter ? imposter : startNode, endOffset); + +                // Override getClientRects to return a rect guaranteed to contain (x, y) +                range.getClientRects = () => [new DOMRect(x - 1, y - 1, 2, 2)]; +                return range; +            }; + +            // Test docRangeFromPoint +            const source = DocumentUtil.getRangeFromPoint(0, 0, { +                deepContentScan: false, +                normalizeCssZoom: true +            }); +            switch (resultType) { +                case 'TextSourceRange': +                    expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceRange.prototype); +                    break; +                case 'TextSourceElement': +                    expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceElement.prototype); +                    break; +                case 'null': +                    expect(source).toStrictEqual(null); +                    break; +                default: +                    expect.unreachable(); +                    break; +            } +            if (source === null) { continue; } + +            // Sentence info +            const terminatorString = '…。..??!!'; +            const terminatorMap = new Map(); +            for (const char of terminatorString) { +                terminatorMap.set(char, [false, true]); +            } +            const quoteArray = [['「', '」'], ['『', '』'], ['\'', '\''], ['"', '"']]; +            const forwardQuoteMap = new Map(); +            const backwardQuoteMap = new Map(); +            for (const [char1, char2] of quoteArray) { +                forwardQuoteMap.set(char1, [char2, false]); +                backwardQuoteMap.set(char2, [char1, false]); +            } + +            // Test docSentenceExtract +            const sentenceActual = DocumentUtil.extractSentence( +                source, +                false, +                sentenceScanExtent, +                terminateAtNewlines, +                terminatorMap, +                forwardQuoteMap, +                backwardQuoteMap +            ).text; +            expect(sentenceActual).toStrictEqual(sentence); + +            // Clean +            source.cleanup(); +        } +    }); +} + +async function testTextSourceRangeSeekFunctions(dom) { +    const document = dom.window.document; + +    test('TextSourceRangeSeekFunctions', async () => { +        for (const testElement of document.querySelectorAll('.test[data-test-type=text-source-range-seek]')) { +        // Get test parameters +            let { +                seekNodeSelector, +                seekNodeIsText, +                seekOffset, +                seekLength, +                seekDirection, +                expectedResultNodeSelector, +                expectedResultNodeIsText, +                expectedResultOffset, +                expectedResultContent +            } = testElement.dataset; + +            seekOffset = parseInt(seekOffset, 10); +            seekLength = parseInt(seekLength, 10); +            expectedResultOffset = parseInt(expectedResultOffset, 10); + +            let seekNode = testElement.querySelector(seekNodeSelector); +            if (seekNodeIsText === 'true') { +                seekNode = seekNode.firstChild; +            } + +            let expectedResultNode = testElement.querySelector(expectedResultNodeSelector); +            if (expectedResultNodeIsText === 'true') { +                expectedResultNode = expectedResultNode.firstChild; +            } + +            const {node, offset, content} = ( +            seekDirection === 'forward' ? +            new DOMTextScanner(seekNode, seekOffset, true, false).seek(seekLength) : +            new DOMTextScanner(seekNode, seekOffset, true, false).seek(-seekLength) +            ); + +            expect(node).toStrictEqual(expectedResultNode); +            expect(offset).toStrictEqual(expectedResultOffset); +            expect(content).toStrictEqual(expectedResultContent); +        } +    }); +} + + +async function main() { +    await testDocument1(); +} + +await main(); diff --git a/test/test-dom-text-scanner.js b/test/dom-text-scanner.test.js index 37017b01..d1b31276 100644 --- a/test/test-dom-text-scanner.js +++ b/test/dom-text-scanner.test.js @@ -16,13 +16,11 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {JSDOM} = require('jsdom'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - +import fs from 'fs'; +import {JSDOM} from 'jsdom'; +import path from 'path'; +import {expect, test} from 'vitest'; +import {DOMTextScanner} from '../ext/js/dom/dom-text-scanner.js';  function createJSDOM(fileName) {      const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); @@ -87,74 +85,77 @@ function createAbsoluteGetComputedStyle(window) {  } -async function testDomTextScanner(dom, {DOMTextScanner}) { +async function testDomTextScanner(dom) {      const document = dom.window.document; -    for (const testElement of document.querySelectorAll('y-test')) { -        let testData = JSON.parse(testElement.dataset.testData); -        if (!Array.isArray(testData)) { -            testData = [testData]; -        } -        for (const testDataItem of testData) { -            let { -                node, -                offset, -                length, -                forcePreserveWhitespace, -                generateLayoutContent, -                reversible, -                expected: { -                    node: expectedNode, -                    offset: expectedOffset, -                    content: expectedContent, -                    remainder: expectedRemainder -                } -            } = testDataItem; - -            node = querySelectorTextNode(testElement, node); -            expectedNode = querySelectorTextNode(testElement, expectedNode); -            // Standard test -            { -                const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent); -                scanner.seek(length); - -                const {node: actualNode1, offset: actualOffset1, content: actualContent1, remainder: actualRemainder1} = scanner; -                assert.strictEqual(actualContent1, expectedContent); -                assert.strictEqual(actualOffset1, expectedOffset); -                assert.strictEqual(actualNode1, expectedNode); -                assert.strictEqual(actualRemainder1, expectedRemainder || 0); +    test('DomTextScanner', () => { +        for (const testElement of document.querySelectorAll('y-test')) { +            let testData = JSON.parse(testElement.dataset.testData); +            if (!Array.isArray(testData)) { +                testData = [testData];              } +            for (const testDataItem of testData) { +                let { +                    node, +                    offset, +                    length, +                    forcePreserveWhitespace, +                    generateLayoutContent, +                    reversible, +                    expected: { +                        node: expectedNode, +                        offset: expectedOffset, +                        content: expectedContent, +                        remainder: expectedRemainder +                    } +                } = testDataItem; + +                node = querySelectorTextNode(testElement, node); +                expectedNode = querySelectorTextNode(testElement, expectedNode); + +                // Standard test +                { +                    const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent); +                    scanner.seek(length); + +                    const {node: actualNode1, offset: actualOffset1, content: actualContent1, remainder: actualRemainder1} = scanner; +                    expect(actualContent1).toStrictEqual(expectedContent); +                    expect(actualOffset1).toStrictEqual(expectedOffset); +                    expect(actualNode1).toStrictEqual(expectedNode); +                    expect(actualRemainder1).toStrictEqual(expectedRemainder || 0); +                } -            // Substring tests -            for (let i = 1; i <= length; ++i) { -                const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent); -                scanner.seek(length - i); +                // Substring tests +                for (let i = 1; i <= length; ++i) { +                    const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent); +                    scanner.seek(length - i); -                const {content: actualContent} = scanner; -                assert.strictEqual(actualContent, expectedContent.substring(0, expectedContent.length - i)); -            } +                    const {content: actualContent} = scanner; +                    expect(actualContent).toStrictEqual(expectedContent.substring(0, expectedContent.length - i)); +                } -            if (reversible === false) { continue; } +                if (reversible === false) { continue; } -            // Reversed test -            { -                const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent); -                scanner.seek(-length); +                // Reversed test +                { +                    const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent); +                    scanner.seek(-length); -                const {content: actualContent} = scanner; -                assert.strictEqual(actualContent, expectedContent); -            } +                    const {content: actualContent} = scanner; +                    expect(actualContent).toStrictEqual(expectedContent); +                } -            // Reversed substring tests -            for (let i = 1; i <= length; ++i) { -                const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent); -                scanner.seek(-(length - i)); +                // Reversed substring tests +                for (let i = 1; i <= length; ++i) { +                    const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent); +                    scanner.seek(-(length - i)); -                const {content: actualContent} = scanner; -                assert.strictEqual(actualContent, expectedContent.substring(i)); +                    const {content: actualContent} = scanner; +                    expect(actualContent).toStrictEqual(expectedContent.substring(i)); +                }              }          } -    } +    });  } @@ -162,17 +163,8 @@ async function testDocument1() {      const dom = createJSDOM(path.join(__dirname, 'data', 'html', 'test-dom-text-scanner.html'));      const window = dom.window;      try { -        const {document, Node, Range} = window; -          window.getComputedStyle = createAbsoluteGetComputedStyle(window); -        const vm = new VM({document, window, Range, Node}); -        vm.execute([ -            'js/data/sandbox/string-util.js', -            'js/dom/dom-text-scanner.js' -        ]); -        const DOMTextScanner = vm.get('DOMTextScanner'); -          await testDomTextScanner(dom, {DOMTextScanner});      } finally {          window.close(); @@ -184,5 +176,4 @@ async function main() {      await testDocument1();  } - -if (require.main === module) { testMain(main); } +await main(); diff --git a/test/hotkey-util.test.js b/test/hotkey-util.test.js new file mode 100644 index 00000000..8666b98b --- /dev/null +++ b/test/hotkey-util.test.js @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {HotkeyUtil} from '../ext/js/input/hotkey-util.js'; + +function testCommandConversions() { +    test('CommandConversions', () => { +        const data = [ +            {os: 'win', command: 'Alt+F', expectedCommand: 'Alt+F', expectedInput: {key: 'KeyF', modifiers: ['alt']}}, +            {os: 'win', command: 'F1',    expectedCommand: 'F1',    expectedInput: {key: 'F1', modifiers: []}}, + +            {os: 'win', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, +            {os: 'win', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, +            {os: 'win', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, + +            {os: 'mac', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, +            {os: 'mac', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'MacCtrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, +            {os: 'mac', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, + +            {os: 'linux', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, +            {os: 'linux', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, +            {os: 'linux', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}} +        ]; + +        const hotkeyUtil = new HotkeyUtil(); +        for (const {command, os, expectedInput, expectedCommand} of data) { +            hotkeyUtil.os = os; +            const input = structuredClone(hotkeyUtil.convertCommandToInput(command)); +            expect(input).toStrictEqual(expectedInput); +            const command2 = hotkeyUtil.convertInputToCommand(input.key, input.modifiers); +            expect(command2).toStrictEqual(expectedCommand); +        } +    }); +} + +function testDisplayNames() { +    test('DisplayNames', () => { +        const data = [ +            {os: 'win', key: null,   modifiers: [], expected: ''}, +            {os: 'win', key: 'KeyF', modifiers: [], expected: 'F'}, +            {os: 'win', key: 'F1',   modifiers: [], expected: 'F1'}, +            {os: 'win', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, +            {os: 'win', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, +            {os: 'win', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, +            {os: 'win', key: null,   modifiers: ['alt'], expected: 'Alt'}, +            {os: 'win', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, +            {os: 'win', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, +            {os: 'win', key: null,   modifiers: ['shift'], expected: 'Shift'}, +            {os: 'win', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, +            {os: 'win', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, +            {os: 'win', key: null,   modifiers: ['meta'], expected: 'Windows'}, +            {os: 'win', key: 'KeyF', modifiers: ['meta'], expected: 'Windows + F'}, +            {os: 'win', key: 'F1',   modifiers: ['meta'], expected: 'Windows + F1'}, +            {os: 'win', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, +            {os: 'win', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, +            {os: 'win', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, + +            {os: 'mac', key: null,   modifiers: [], expected: ''}, +            {os: 'mac', key: 'KeyF', modifiers: [], expected: 'F'}, +            {os: 'mac', key: 'F1',   modifiers: [], expected: 'F1'}, +            {os: 'mac', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, +            {os: 'mac', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, +            {os: 'mac', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, +            {os: 'mac', key: null,   modifiers: ['alt'], expected: 'Opt'}, +            {os: 'mac', key: 'KeyF', modifiers: ['alt'], expected: 'Opt + F'}, +            {os: 'mac', key: 'F1',   modifiers: ['alt'], expected: 'Opt + F1'}, +            {os: 'mac', key: null,   modifiers: ['shift'], expected: 'Shift'}, +            {os: 'mac', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, +            {os: 'mac', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, +            {os: 'mac', key: null,   modifiers: ['meta'], expected: 'Cmd'}, +            {os: 'mac', key: 'KeyF', modifiers: ['meta'], expected: 'Cmd + F'}, +            {os: 'mac', key: 'F1',   modifiers: ['meta'], expected: 'Cmd + F1'}, +            {os: 'mac', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, +            {os: 'mac', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, +            {os: 'mac', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, + +            {os: 'linux', key: null,   modifiers: [], expected: ''}, +            {os: 'linux', key: 'KeyF', modifiers: [], expected: 'F'}, +            {os: 'linux', key: 'F1',   modifiers: [], expected: 'F1'}, +            {os: 'linux', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, +            {os: 'linux', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, +            {os: 'linux', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, +            {os: 'linux', key: null,   modifiers: ['alt'], expected: 'Alt'}, +            {os: 'linux', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, +            {os: 'linux', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, +            {os: 'linux', key: null,   modifiers: ['shift'], expected: 'Shift'}, +            {os: 'linux', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, +            {os: 'linux', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, +            {os: 'linux', key: null,   modifiers: ['meta'], expected: 'Super'}, +            {os: 'linux', key: 'KeyF', modifiers: ['meta'], expected: 'Super + F'}, +            {os: 'linux', key: 'F1',   modifiers: ['meta'], expected: 'Super + F1'}, +            {os: 'linux', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, +            {os: 'linux', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, +            {os: 'linux', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, + +            {os: 'unknown', key: null,   modifiers: [], expected: ''}, +            {os: 'unknown', key: 'KeyF', modifiers: [], expected: 'F'}, +            {os: 'unknown', key: 'F1',   modifiers: [], expected: 'F1'}, +            {os: 'unknown', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, +            {os: 'unknown', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, +            {os: 'unknown', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, +            {os: 'unknown', key: null,   modifiers: ['alt'], expected: 'Alt'}, +            {os: 'unknown', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, +            {os: 'unknown', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, +            {os: 'unknown', key: null,   modifiers: ['shift'], expected: 'Shift'}, +            {os: 'unknown', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, +            {os: 'unknown', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, +            {os: 'unknown', key: null,   modifiers: ['meta'], expected: 'Meta'}, +            {os: 'unknown', key: 'KeyF', modifiers: ['meta'], expected: 'Meta + F'}, +            {os: 'unknown', key: 'F1',   modifiers: ['meta'], expected: 'Meta + F1'}, +            {os: 'unknown', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, +            {os: 'unknown', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, +            {os: 'unknown', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'} +        ]; + +        const hotkeyUtil = new HotkeyUtil(); +        for (const {os, key, modifiers, expected} of data) { +            hotkeyUtil.os = os; +            const displayName = hotkeyUtil.getInputDisplayValue(key, modifiers); +            expect(displayName).toStrictEqual(expected); +        } +    }); +} + +function testSortModifiers() { +    test('SortModifiers', () => { +        const data = [ +            {modifiers: [], expected: []}, +            {modifiers: ['shift', 'alt', 'ctrl', 'mouse4', 'meta', 'mouse1', 'mouse0'], expected: ['meta', 'ctrl', 'alt', 'shift', 'mouse0', 'mouse1', 'mouse4']} +        ]; + +        const hotkeyUtil = new HotkeyUtil(); +        for (const {modifiers, expected} of data) { +            const modifiers2 = hotkeyUtil.sortModifiers(modifiers); +            expect(modifiers2).toStrictEqual(modifiers); +            expect(modifiers2).toStrictEqual(expected); +        } +    }); +} + + +function main() { +    testCommandConversions(); +    testDisplayNames(); +    testSortModifiers(); +} + +main(); diff --git a/test/japanese-util.test.js b/test/japanese-util.test.js new file mode 100644 index 00000000..47da4ccb --- /dev/null +++ b/test/japanese-util.test.js @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {TextSourceMap} from '../ext/js/general/text-source-map.js'; +import {JapaneseUtil} from '../ext/js/language/sandbox/japanese-util.js'; +import * as wanakana from '../ext/lib/wanakana.js'; + +const jp = new JapaneseUtil(wanakana); + +function testIsCodePointKanji() { +    test('isCodePointKanji', () => { +        const data = [ +            ['力方', true], +            ['\u53f1\u{20b9f}', true], +            ['かたカタ々kata、。?,.?', false], +            ['逸逸', true] +        ]; + +        for (const [characters, expected] of data) { +            for (const character of characters) { +                const codePoint = character.codePointAt(0); +                const actual = jp.isCodePointKanji(codePoint); +                expect(actual).toStrictEqual(expected); // `isCodePointKanji failed for ${character} (\\u{${codePoint.toString(16)}})` +            } +        } +    }); +} + +function testIsCodePointKana() { +    test('isCodePointKana', () => { +        const data = [ +            ['かたカタ', true], +            ['力方々kata、。?,.?', false], +            ['\u53f1\u{20b9f}', false] +        ]; + +        for (const [characters, expected] of data) { +            for (const character of characters) { +                const codePoint = character.codePointAt(0); +                const actual = jp.isCodePointKana(codePoint); +                expect(actual).toStrictEqual(expected); // `isCodePointKana failed for ${character} (\\u{${codePoint.toString(16)}})` +            } +        } +    }); +} + +function testIsCodePointJapanese() { +    test('isCodePointJapanese', () => { +        const data = [ +            ['かたカタ力方々、。?', true], +            ['\u53f1\u{20b9f}', true], +            ['kata,.?', false], +            ['逸逸', true] +        ]; + +        for (const [characters, expected] of data) { +            for (const character of characters) { +                const codePoint = character.codePointAt(0); +                const actual = jp.isCodePointJapanese(codePoint); +                expect(actual).toStrictEqual(expected); // `isCodePointJapanese failed for ${character} (\\u{${codePoint.toString(16)}})` +            } +        } +    }); +} + +function testIsStringEntirelyKana() { +    test('isStringEntirelyKana', () => { +        const data = [ +            ['かたかな', true], +            ['カタカナ', true], +            ['ひらがな', true], +            ['ヒラガナ', true], +            ['カタカナひらがな', true], +            ['かたカタ力方々、。?', false], +            ['\u53f1\u{20b9f}', false], +            ['kata,.?', false], +            ['かたカタ力方々、。?invalid', false], +            ['\u53f1\u{20b9f}invalid', false], +            ['kata,.?かた', false] +        ]; + +        for (const [string, expected] of data) { +            expect(jp.isStringEntirelyKana(string)).toStrictEqual(expected); +        } +    }); +} + +function testIsStringPartiallyJapanese() { +    test('isStringPartiallyJapanese', () => { +        const data = [ +            ['かたかな', true], +            ['カタカナ', true], +            ['ひらがな', true], +            ['ヒラガナ', true], +            ['カタカナひらがな', true], +            ['かたカタ力方々、。?', true], +            ['\u53f1\u{20b9f}', true], +            ['kata,.?', false], +            ['かたカタ力方々、。?invalid', true], +            ['\u53f1\u{20b9f}invalid', true], +            ['kata,.?かた', true], +            ['逸逸', true] +        ]; + +        for (const [string, expected] of data) { +            expect(jp.isStringPartiallyJapanese(string)).toStrictEqual(expected); +        } +    }); +} + +function testConvertKatakanaToHiragana() { +    test('convertKatakanaToHiragana', () => { +        const data = [ +            ['かたかな', 'かたかな'], +            ['ひらがな', 'ひらがな'], +            ['カタカナ', 'かたかな'], +            ['ヒラガナ', 'ひらがな'], +            ['カタカナかたかな', 'かたかなかたかな'], +            ['ヒラガナひらがな', 'ひらがなひらがな'], +            ['chikaraちからチカラ力', 'chikaraちからちから力'], +            ['katakana', 'katakana'], +            ['hiragana', 'hiragana'], +            ['カーナー', 'かあなあ'], +            ['カーナー', 'かーなー', true] +        ]; + +        for (const [string, expected, keepProlongedSoundMarks=false] of data) { +            expect(jp.convertKatakanaToHiragana(string, keepProlongedSoundMarks)).toStrictEqual(expected); +        } +    }); +} + +function testConvertHiraganaToKatakana() { +    test('ConvertHiraganaToKatakana', () => { +        const data = [ +            ['かたかな', 'カタカナ'], +            ['ひらがな', 'ヒラガナ'], +            ['カタカナ', 'カタカナ'], +            ['ヒラガナ', 'ヒラガナ'], +            ['カタカナかたかな', 'カタカナカタカナ'], +            ['ヒラガナひらがな', 'ヒラガナヒラガナ'], +            ['chikaraちからチカラ力', 'chikaraチカラチカラ力'], +            ['katakana', 'katakana'], +            ['hiragana', 'hiragana'] +        ]; + +        for (const [string, expected] of data) { +            expect(jp.convertHiraganaToKatakana(string)).toStrictEqual(expected); +        } +    }); +} + +function testConvertToRomaji() { +    test('ConvertToRomaji', () => { +        const data = [ +            ['かたかな', 'katakana'], +            ['ひらがな', 'hiragana'], +            ['カタカナ', 'katakana'], +            ['ヒラガナ', 'hiragana'], +            ['カタカナかたかな', 'katakanakatakana'], +            ['ヒラガナひらがな', 'hiraganahiragana'], +            ['chikaraちからチカラ力', 'chikarachikarachikara力'], +            ['katakana', 'katakana'], +            ['hiragana', 'hiragana'] +        ]; + +        for (const [string, expected] of data) { +            expect(jp.convertToRomaji(string)).toStrictEqual(expected); +        } +    }); +} + +function testConvertNumericToFullWidth() { +    test('ConvertNumericToFullWidth', () => { +        const data = [ +            ['0123456789', '0123456789'], +            ['abcdefghij', 'abcdefghij'], +            ['カタカナ', 'カタカナ'], +            ['ひらがな', 'ひらがな'] +        ]; + +        for (const [string, expected] of data) { +            expect(jp.convertNumericToFullWidth(string)).toStrictEqual(expected); +        } +    }); +} + +function testConvertHalfWidthKanaToFullWidth() { +    test('ConvertHalfWidthKanaToFullWidth', () => { +        const data = [ +            ['0123456789', '0123456789'], +            ['abcdefghij', 'abcdefghij'], +            ['カタカナ', 'カタカナ'], +            ['ひらがな', 'ひらがな'], +            ['カキ', 'カキ', [1, 1]], +            ['ガキ', 'ガキ', [2, 1]], +            ['ニホン', 'ニホン', [1, 1, 1]], +            ['ニッポン', 'ニッポン', [1, 1, 2, 1]] +        ]; + +        for (const [string, expected, expectedSourceMapping] of data) { +            const sourceMap = new TextSourceMap(string); +            const actual1 = jp.convertHalfWidthKanaToFullWidth(string, null); +            const actual2 = jp.convertHalfWidthKanaToFullWidth(string, sourceMap); +            expect(actual1).toStrictEqual(expected); +            expect(actual2).toStrictEqual(expected); +            if (typeof expectedSourceMapping !== 'undefined') { +                expect(sourceMap.equals(new TextSourceMap(string, expectedSourceMapping))).toBe(true); +            } +        } +    }); +} + +function testConvertAlphabeticToKana() { +    test('ConvertAlphabeticToKana', () => { +        const data = [ +            ['0123456789', '0123456789'], +            ['abcdefghij', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]], +            ['ABCDEFGHIJ', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]], // wanakana.toHiragana converts text to lower case +            ['カタカナ', 'カタカナ'], +            ['ひらがな', 'ひらがな'], +            ['chikara', 'ちから', [3, 2, 2]], +            ['CHIKARA', 'ちから', [3, 2, 2]] +        ]; + +        for (const [string, expected, expectedSourceMapping] of data) { +            const sourceMap = new TextSourceMap(string); +            const actual1 = jp.convertAlphabeticToKana(string, null); +            const actual2 = jp.convertAlphabeticToKana(string, sourceMap); +            expect(actual1).toStrictEqual(expected); +            expect(actual2).toStrictEqual(expected); +            if (typeof expectedSourceMapping !== 'undefined') { +                expect(sourceMap.equals(new TextSourceMap(string, expectedSourceMapping))).toBe(true); +            } +        } +    }); +} + +function testDistributeFurigana() { +    test('DistributeFurigana', () => { +        const data = [ +            [ +                ['有り難う', 'ありがとう'], +                [ +                    {text: '有', reading: 'あ'}, +                    {text: 'り', reading: ''}, +                    {text: '難', reading: 'がと'}, +                    {text: 'う', reading: ''} +                ] +            ], +            [ +                ['方々', 'かたがた'], +                [ +                    {text: '方々', reading: 'かたがた'} +                ] +            ], +            [ +                ['お祝い', 'おいわい'], +                [ +                    {text: 'お', reading: ''}, +                    {text: '祝', reading: 'いわ'}, +                    {text: 'い', reading: ''} +                ] +            ], +            [ +                ['美味しい', 'おいしい'], +                [ +                    {text: '美味', reading: 'おい'}, +                    {text: 'しい', reading: ''} +                ] +            ], +            [ +                ['食べ物', 'たべもの'], +                [ +                    {text: '食', reading: 'た'}, +                    {text: 'べ', reading: ''}, +                    {text: '物', reading: 'もの'} +                ] +            ], +            [ +                ['試し切り', 'ためしぎり'], +                [ +                    {text: '試', reading: 'ため'}, +                    {text: 'し', reading: ''}, +                    {text: '切', reading: 'ぎ'}, +                    {text: 'り', reading: ''} +                ] +            ], +            // Ambiguous +            [ +                ['飼い犬', 'かいいぬ'], +                [ +                    {text: '飼い犬', reading: 'かいいぬ'} +                ] +            ], +            [ +                ['長い間', 'ながいあいだ'], +                [ +                    {text: '長い間', reading: 'ながいあいだ'} +                ] +            ], +            // Same/empty reading +            [ +                ['飼い犬', ''], +                [ +                    {text: '飼い犬', reading: ''} +                ] +            ], +            [ +                ['かいいぬ', 'かいいぬ'], +                [ +                    {text: 'かいいぬ', reading: ''} +                ] +            ], +            [ +                ['かいぬ', 'かいぬ'], +                [ +                    {text: 'かいぬ', reading: ''} +                ] +            ], +            // Misc +            [ +                ['月', 'か'], +                [ +                    {text: '月', reading: 'か'} +                ] +            ], +            [ +                ['月', 'カ'], +                [ +                    {text: '月', reading: 'カ'} +                ] +            ], +            // Mismatched kana readings +            [ +                ['有り難う', 'アリガトウ'], +                [ +                    {text: '有', reading: 'ア'}, +                    {text: 'り', reading: 'リ'}, +                    {text: '難', reading: 'ガト'}, +                    {text: 'う', reading: 'ウ'} +                ] +            ], +            [ +                ['ありがとう', 'アリガトウ'], +                [ +                    {text: 'ありがとう', reading: 'アリガトウ'} +                ] +            ], +            // Mismatched kana readings (real examples) +            [ +                ['カ月', 'かげつ'], +                [ +                    {text: 'カ', reading: 'か'}, +                    {text: '月', reading: 'げつ'} +                ] +            ], +            [ +                ['序ノ口', 'じょのくち'], +                [ +                    {text: '序', reading: 'じょ'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '口', reading: 'くち'} +                ] +            ], +            [ +                ['スズメの涙', 'すずめのなみだ'], +                [ +                    {text: 'スズメ', reading: 'すずめ'}, +                    {text: 'の', reading: ''}, +                    {text: '涙', reading: 'なみだ'} +                ] +            ], +            [ +                ['二カ所', 'にかしょ'], +                [ +                    {text: '二', reading: 'に'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '所', reading: 'しょ'} +                ] +            ], +            [ +                ['八ツ橋', 'やつはし'], +                [ +                    {text: '八', reading: 'や'}, +                    {text: 'ツ', reading: 'つ'}, +                    {text: '橋', reading: 'はし'} +                ] +            ], +            [ +                ['八ツ橋', 'やつはし'], +                [ +                    {text: '八', reading: 'や'}, +                    {text: 'ツ', reading: 'つ'}, +                    {text: '橋', reading: 'はし'} +                ] +            ], +            [ +                ['一カ月', 'いっかげつ'], +                [ +                    {text: '一', reading: 'いっ'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '月', reading: 'げつ'} +                ] +            ], +            [ +                ['一カ所', 'いっかしょ'], +                [ +                    {text: '一', reading: 'いっ'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '所', reading: 'しょ'} +                ] +            ], +            [ +                ['カ所', 'かしょ'], +                [ +                    {text: 'カ', reading: 'か'}, +                    {text: '所', reading: 'しょ'} +                ] +            ], +            [ +                ['数カ月', 'すうかげつ'], +                [ +                    {text: '数', reading: 'すう'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '月', reading: 'げつ'} +                ] +            ], +            [ +                ['くノ一', 'くのいち'], +                [ +                    {text: 'く', reading: ''}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '一', reading: 'いち'} +                ] +            ], +            [ +                ['くノ一', 'くのいち'], +                [ +                    {text: 'く', reading: ''}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '一', reading: 'いち'} +                ] +            ], +            [ +                ['数カ国', 'すうかこく'], +                [ +                    {text: '数', reading: 'すう'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '国', reading: 'こく'} +                ] +            ], +            [ +                ['数カ所', 'すうかしょ'], +                [ +                    {text: '数', reading: 'すう'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '所', reading: 'しょ'} +                ] +            ], +            [ +                ['壇ノ浦の戦い', 'だんのうらのたたかい'], +                [ +                    {text: '壇', reading: 'だん'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '浦', reading: 'うら'}, +                    {text: 'の', reading: ''}, +                    {text: '戦', reading: 'たたか'}, +                    {text: 'い', reading: ''} +                ] +            ], +            [ +                ['壇ノ浦の戦', 'だんのうらのたたかい'], +                [ +                    {text: '壇', reading: 'だん'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '浦', reading: 'うら'}, +                    {text: 'の', reading: ''}, +                    {text: '戦', reading: 'たたかい'} +                ] +            ], +            [ +                ['序ノ口格', 'じょのくちかく'], +                [ +                    {text: '序', reading: 'じょ'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '口格', reading: 'くちかく'} +                ] +            ], +            [ +                ['二カ国語', 'にかこくご'], +                [ +                    {text: '二', reading: 'に'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '国語', reading: 'こくご'} +                ] +            ], +            [ +                ['カ国', 'かこく'], +                [ +                    {text: 'カ', reading: 'か'}, +                    {text: '国', reading: 'こく'} +                ] +            ], +            [ +                ['カ国語', 'かこくご'], +                [ +                    {text: 'カ', reading: 'か'}, +                    {text: '国語', reading: 'こくご'} +                ] +            ], +            [ +                ['壇ノ浦の合戦', 'だんのうらのかっせん'], +                [ +                    {text: '壇', reading: 'だん'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '浦', reading: 'うら'}, +                    {text: 'の', reading: ''}, +                    {text: '合戦', reading: 'かっせん'} +                ] +            ], +            [ +                ['一タ偏', 'いちたへん'], +                [ +                    {text: '一', reading: 'いち'}, +                    {text: 'タ', reading: 'た'}, +                    {text: '偏', reading: 'へん'} +                ] +            ], +            [ +                ['ル又', 'るまた'], +                [ +                    {text: 'ル', reading: 'る'}, +                    {text: '又', reading: 'また'} +                ] +            ], +            [ +                ['ノ木偏', 'のぎへん'], +                [ +                    {text: 'ノ', reading: 'の'}, +                    {text: '木偏', reading: 'ぎへん'} +                ] +            ], +            [ +                ['一ノ貝', 'いちのかい'], +                [ +                    {text: '一', reading: 'いち'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '貝', reading: 'かい'} +                ] +            ], +            [ +                ['虎ノ門事件', 'とらのもんじけん'], +                [ +                    {text: '虎', reading: 'とら'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '門事件', reading: 'もんじけん'} +                ] +            ], +            [ +                ['教育ニ関スル勅語', 'きょういくにかんするちょくご'], +                [ +                    {text: '教育', reading: 'きょういく'}, +                    {text: 'ニ', reading: 'に'}, +                    {text: '関', reading: 'かん'}, +                    {text: 'スル', reading: 'する'}, +                    {text: '勅語', reading: 'ちょくご'} +                ] +            ], +            [ +                ['二カ年', 'にかねん'], +                [ +                    {text: '二', reading: 'に'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['三カ年', 'さんかねん'], +                [ +                    {text: '三', reading: 'さん'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['四カ年', 'よんかねん'], +                [ +                    {text: '四', reading: 'よん'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['五カ年', 'ごかねん'], +                [ +                    {text: '五', reading: 'ご'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['六カ年', 'ろっかねん'], +                [ +                    {text: '六', reading: 'ろっ'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['七カ年', 'ななかねん'], +                [ +                    {text: '七', reading: 'なな'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['八カ年', 'はちかねん'], +                [ +                    {text: '八', reading: 'はち'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['九カ年', 'きゅうかねん'], +                [ +                    {text: '九', reading: 'きゅう'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['十カ年', 'じゅうかねん'], +                [ +                    {text: '十', reading: 'じゅう'}, +                    {text: 'カ', reading: 'か'}, +                    {text: '年', reading: 'ねん'} +                ] +            ], +            [ +                ['鏡ノ間', 'かがみのま'], +                [ +                    {text: '鏡', reading: 'かがみ'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '間', reading: 'ま'} +                ] +            ], +            [ +                ['鏡ノ間', 'かがみのま'], +                [ +                    {text: '鏡', reading: 'かがみ'}, +                    {text: 'ノ', reading: 'の'}, +                    {text: '間', reading: 'ま'} +                ] +            ], +            [ +                ['ページ違反', 'ぺーじいはん'], +                [ +                    {text: 'ペ', reading: 'ぺ'}, +                    {text: 'ー', reading: ''}, +                    {text: 'ジ', reading: 'じ'}, +                    {text: '違反', reading: 'いはん'} +                ] +            ], +            // Mismatched kana +            [ +                ['サボる', 'サボル'], +                [ +                    {text: 'サボ', reading: ''}, +                    {text: 'る', reading: 'ル'} +                ] +            ], +            // Reading starts with term, but has remainder characters +            [ +                ['シック', 'シック・ビルしょうこうぐん'], +                [ +                    {text: 'シック', reading: 'シック・ビルしょうこうぐん'} +                ] +            ], +            // Kanji distribution tests +            [ +                ['逸らす', 'そらす'], +                [ +                    {text: '逸', reading: 'そ'}, +                    {text: 'らす', reading: ''} +                ] +            ], +            [ +                ['逸らす', 'そらす'], +                [ +                    {text: '逸', reading: 'そ'}, +                    {text: 'らす', reading: ''} +                ] +            ] +        ]; + +        for (const [[term, reading], expected] of data) { +            const actual = jp.distributeFurigana(term, reading); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testDistributeFuriganaInflected() { +    test('DistributeFuriganaInflected', () => { +        const data = [ +            [ +                ['美味しい', 'おいしい', '美味しかた'], +                [ +                    {text: '美味', reading: 'おい'}, +                    {text: 'しかた', reading: ''} +                ] +            ], +            [ +                ['食べる', 'たべる', '食べた'], +                [ +                    {text: '食', reading: 'た'}, +                    {text: 'べた', reading: ''} +                ] +            ], +            [ +                ['迄に', 'までに', 'までに'], +                [ +                    {text: 'までに', reading: ''} +                ] +            ], +            [ +                ['行う', 'おこなう', 'おこなわなかった'], +                [ +                    {text: 'おこなわなかった', reading: ''} +                ] +            ], +            [ +                ['いい', 'いい', 'イイ'], +                [ +                    {text: 'イイ', reading: ''} +                ] +            ], +            [ +                ['否か', 'いなか', '否カ'], +                [ +                    {text: '否', reading: 'いな'}, +                    {text: 'カ', reading: 'か'} +                ] +            ] +        ]; + +        for (const [[term, reading, source], expected] of data) { +            const actual = jp.distributeFuriganaInflected(term, reading, source); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testCollapseEmphaticSequences() { +    test('CollapseEmphaticSequences', () => { +        const data = [ +            [['かこい', false], ['かこい', [1, 1, 1]]], +            [['かこい', true], ['かこい', [1, 1, 1]]], +            [['かっこい', false], ['かっこい', [1, 1, 1, 1]]], +            [['かっこい', true], ['かこい', [2, 1, 1]]], +            [['かっっこい', false], ['かっこい', [1, 2, 1, 1]]], +            [['かっっこい', true], ['かこい', [3, 1, 1]]], +            [['かっっっこい', false], ['かっこい', [1, 3, 1, 1]]], +            [['かっっっこい', true], ['かこい', [4, 1, 1]]], + +            [['こい', false], ['こい', [1, 1]]], +            [['こい', true], ['こい', [1, 1]]], +            [['っこい', false], ['っこい', [1, 1, 1]]], +            [['っこい', true], ['こい', [2, 1]]], +            [['っっこい', false], ['っこい', [2, 1, 1]]], +            [['っっこい', true], ['こい', [3, 1]]], +            [['っっっこい', false], ['っこい', [3, 1, 1]]], +            [['っっっこい', true], ['こい', [4, 1]]], + +            [['すごい', false], ['すごい', [1, 1, 1]]], +            [['すごい', true], ['すごい', [1, 1, 1]]], +            [['すごーい', false], ['すごーい', [1, 1, 1, 1]]], +            [['すごーい', true], ['すごい', [1, 2, 1]]], +            [['すごーーい', false], ['すごーい', [1, 1, 2, 1]]], +            [['すごーーい', true], ['すごい', [1, 3, 1]]], +            [['すっごーい', false], ['すっごーい', [1, 1, 1, 1, 1]]], +            [['すっごーい', true], ['すごい', [2, 2, 1]]], +            [['すっっごーーい', false], ['すっごーい', [1, 2, 1, 2, 1]]], +            [['すっっごーーい', true], ['すごい', [3, 3, 1]]], + +            [['', false], ['', []]], +            [['', true], ['', []]], +            [['っ', false], ['っ', [1]]], +            [['っ', true], ['', [1]]], +            [['っっ', false], ['っ', [2]]], +            [['っっ', true], ['', [2]]], +            [['っっっ', false], ['っ', [3]]], +            [['っっっ', true], ['', [3]]] +        ]; + +        for (const [[text, fullCollapse], [expected, expectedSourceMapping]] of data) { +            const sourceMap = new TextSourceMap(text); +            const actual1 = jp.collapseEmphaticSequences(text, fullCollapse, null); +            const actual2 = jp.collapseEmphaticSequences(text, fullCollapse, sourceMap); +            expect(actual1).toStrictEqual(expected); +            expect(actual2).toStrictEqual(expected); +            if (typeof expectedSourceMapping !== 'undefined') { +                expect(sourceMap.equals(new TextSourceMap(text, expectedSourceMapping))).toBe(true); +            } +        } +    }); +} + +function testIsMoraPitchHigh() { +    test('IsMoraPitchHigh', () => { +        const data = [ +            [[0, 0], false], +            [[1, 0], true], +            [[2, 0], true], +            [[3, 0], true], + +            [[0, 1], true], +            [[1, 1], false], +            [[2, 1], false], +            [[3, 1], false], + +            [[0, 2], false], +            [[1, 2], true], +            [[2, 2], false], +            [[3, 2], false], + +            [[0, 3], false], +            [[1, 3], true], +            [[2, 3], true], +            [[3, 3], false], + +            [[0, 4], false], +            [[1, 4], true], +            [[2, 4], true], +            [[3, 4], true] +        ]; + +        for (const [[moraIndex, pitchAccentDownstepPosition], expected] of data) { +            const actual = jp.isMoraPitchHigh(moraIndex, pitchAccentDownstepPosition); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testGetKanaMorae() { +    test('GetKanaMorae', () => { +        const data = [ +            ['かこ', ['か', 'こ']], +            ['かっこ', ['か', 'っ', 'こ']], +            ['カコ', ['カ', 'コ']], +            ['カッコ', ['カ', 'ッ', 'コ']], +            ['コート', ['コ', 'ー', 'ト']], +            ['ちゃんと', ['ちゃ', 'ん', 'と']], +            ['とうきょう', ['と', 'う', 'きょ', 'う']], +            ['ぎゅう', ['ぎゅ', 'う']], +            ['ディスコ', ['ディ', 'ス', 'コ']] +        ]; + +        for (const [text, expected] of data) { +            const actual = jp.getKanaMorae(text); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + + +function main() { +    testIsCodePointKanji(); +    testIsCodePointKana(); +    testIsCodePointJapanese(); +    testIsStringEntirelyKana(); +    testIsStringPartiallyJapanese(); +    testConvertKatakanaToHiragana(); +    testConvertHiraganaToKatakana(); +    testConvertToRomaji(); +    testConvertNumericToFullWidth(); +    testConvertHalfWidthKanaToFullWidth(); +    testConvertAlphabeticToKana(); +    testDistributeFurigana(); +    testDistributeFuriganaInflected(); +    testCollapseEmphaticSequences(); +    testIsMoraPitchHigh(); +    testGetKanaMorae(); +} + +main(); diff --git a/test/test-jsdom.js b/test/jsdom.test.js index 5078c240..c53f374e 100644 --- a/test/test-jsdom.js +++ b/test/jsdom.test.js @@ -16,8 +16,8 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const assert = require('assert'); -const {testMain} = require('../dev/util'); +import {JSDOM} from 'jsdom'; +import {expect, test} from 'vitest';  /**   * This function tests the following bug: @@ -25,17 +25,18 @@ const {testMain} = require('../dev/util');   * - https://github.com/dperini/nwsapi/issues/48   */  function testJSDOMSelectorBug() { +    test('JSDOMSelectorBug', () => {      // nwsapi is used by JSDOM -    const {JSDOM} = require('jsdom'); -    const dom = new JSDOM(); -    const {document} = dom.window; -    const div = document.createElement('div'); -    div.innerHTML = '<div class="b"><div class="c"></div></div>'; -    const c = div.querySelector('.c'); -    assert.doesNotThrow(() => { c.matches('.a:nth-last-of-type(1) .b .c'); }); +        const dom = new JSDOM(); +        const {document} = dom.window; +        const div = document.createElement('div'); +        div.innerHTML = '<div class="b"><div class="c"></div></div>'; +        const c = div.querySelector('.c'); +        expect(() => c.matches('.a:nth-last-of-type(1) .b .c')).not.toThrow(); +    });  } -function testJSDOM() { +export function testJSDOM() {      testJSDOMSelectorBug();  } @@ -43,8 +44,4 @@ function main() {      testJSDOM();  } -module.exports = { -    testJSDOM -}; - -if (require.main === module) { testMain(main); } +main(); diff --git a/test/json-schema.test.js b/test/json-schema.test.js new file mode 100644 index 00000000..5370e8da --- /dev/null +++ b/test/json-schema.test.js @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {JsonSchema} from '../ext/js/data/json-schema.js'; + +function schemaValidate(schema, value) { +    return new JsonSchema(schema).isValid(value); +} + +function getValidValueOrDefault(schema, value) { +    return new JsonSchema(schema).getValidValueOrDefault(value); +} + +function createProxy(schema, value) { +    return new JsonSchema(schema).createProxy(value); +} + +function clone(value) { +    return JSON.parse(JSON.stringify(value)); +} + + +function testValidate1() { +    test('Validate1', () => { +        const schema = { +            allOf: [ +                { +                    type: 'number' +                }, +                { +                    anyOf: [ +                        {minimum: 10, maximum: 100}, +                        {minimum: -100, maximum: -10} +                    ] +                }, +                { +                    oneOf: [ +                        {multipleOf: 3}, +                        {multipleOf: 5} +                    ] +                }, +                { +                    not: [ +                        {multipleOf: 20} +                    ] +                } +            ] +        }; + +        const jsValidate = (value) => { +            return ( +                typeof value === 'number' && +            ( +                (value >= 10 && value <= 100) || +                (value >= -100 && value <= -10) +            ) && +            ( +                ( +                    (value % 3) === 0 || +                    (value % 5) === 0 +                ) && +                (value % 15) !== 0 +            ) && +            (value % 20) !== 0 +            ); +        }; + +        for (let i = -111; i <= 111; i++) { +            const actual = schemaValidate(schema, i); +            const expected = jsValidate(i); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testValidate2() { +    test('Validate2', () => { +        const data = [ +        // String tests +            { +                schema: { +                    type: 'string' +                }, +                inputs: [ +                    {expected: false, value: null}, +                    {expected: false, value: void 0}, +                    {expected: false, value: 0}, +                    {expected: false, value: {}}, +                    {expected: false, value: []}, +                    {expected: true,  value: ''} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    minLength: 2 +                }, +                inputs: [ +                    {expected: false, value: ''}, +                    {expected: false,  value: '1'}, +                    {expected: true,  value: '12'}, +                    {expected: true,  value: '123'} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    maxLength: 2 +                }, +                inputs: [ +                    {expected: true,  value: ''}, +                    {expected: true,  value: '1'}, +                    {expected: true,  value: '12'}, +                    {expected: false, value: '123'} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    pattern: 'test' +                }, +                inputs: [ +                    {expected: false, value: ''}, +                    {expected: true,  value: 'test'}, +                    {expected: false, value: 'TEST'}, +                    {expected: true,  value: 'ABCtestDEF'}, +                    {expected: false, value: 'ABCTESTDEF'} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    pattern: '^test$' +                }, +                inputs: [ +                    {expected: false, value: ''}, +                    {expected: true,  value: 'test'}, +                    {expected: false, value: 'TEST'}, +                    {expected: false, value: 'ABCtestDEF'}, +                    {expected: false, value: 'ABCTESTDEF'} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    pattern: '^test$', +                    patternFlags: 'i' +                }, +                inputs: [ +                    {expected: false, value: ''}, +                    {expected: true,  value: 'test'}, +                    {expected: true,  value: 'TEST'}, +                    {expected: false, value: 'ABCtestDEF'}, +                    {expected: false, value: 'ABCTESTDEF'} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    pattern: '*' +                }, +                inputs: [ +                    {expected: false, value: ''} +                ] +            }, +            { +                schema: { +                    type: 'string', +                    pattern: '.', +                    patternFlags: '?' +                }, +                inputs: [ +                    {expected: false, value: ''} +                ] +            }, + +            // Const tests +            { +                schema: { +                    const: 32 +                }, +                inputs: [ +                    {expected: true,  value: 32}, +                    {expected: false, value: 0}, +                    {expected: false, value: '32'}, +                    {expected: false, value: null}, +                    {expected: false, value: {a: 'b'}}, +                    {expected: false, value: [1, 2, 3]} +                ] +            }, +            { +                schema: { +                    const: '32' +                }, +                inputs: [ +                    {expected: false, value: 32}, +                    {expected: false, value: 0}, +                    {expected: true,  value: '32'}, +                    {expected: false, value: null}, +                    {expected: false, value: {a: 'b'}}, +                    {expected: false, value: [1, 2, 3]} +                ] +            }, +            { +                schema: { +                    const: null +                }, +                inputs: [ +                    {expected: false, value: 32}, +                    {expected: false, value: 0}, +                    {expected: false, value: '32'}, +                    {expected: true,  value: null}, +                    {expected: false, value: {a: 'b'}}, +                    {expected: false, value: [1, 2, 3]} +                ] +            }, +            { +                schema: { +                    const: {a: 'b'} +                }, +                inputs: [ +                    {expected: false, value: 32}, +                    {expected: false, value: 0}, +                    {expected: false, value: '32'}, +                    {expected: false, value: null}, +                    {expected: false, value: {a: 'b'}}, +                    {expected: false, value: [1, 2, 3]} +                ] +            }, +            { +                schema: { +                    const: [1, 2, 3] +                }, +                inputs: [ +                    {expected: false, value: 32}, +                    {expected: false, value: 0}, +                    {expected: false,  value: '32'}, +                    {expected: false, value: null}, +                    {expected: false, value: {a: 'b'}}, +                    {expected: false, value: [1, 2, 3]} +                ] +            }, + +            // Array contains tests +            { +                schema: { +                    type: 'array', +                    contains: {const: 32} +                }, +                inputs: [ +                    {expected: false, value: []}, +                    {expected: true,  value: [32]}, +                    {expected: true,  value: [1, 32]}, +                    {expected: true,  value: [1, 32, 1]}, +                    {expected: false, value: [33]}, +                    {expected: false, value: [1, 33]}, +                    {expected: false, value: [1, 33, 1]} +                ] +            }, + +            // Number limits tests +            { +                schema: { +                    type: 'number', +                    minimum: 0 +                }, +                inputs: [ +                    {expected: false, value: -1}, +                    {expected: true,  value: 0}, +                    {expected: true,  value: 1} +                ] +            }, +            { +                schema: { +                    type: 'number', +                    exclusiveMinimum: 0 +                }, +                inputs: [ +                    {expected: false, value: -1}, +                    {expected: false, value: 0}, +                    {expected: true,  value: 1} +                ] +            }, +            { +                schema: { +                    type: 'number', +                    maximum: 0 +                }, +                inputs: [ +                    {expected: true,  value: -1}, +                    {expected: true,  value: 0}, +                    {expected: false, value: 1} +                ] +            }, +            { +                schema: { +                    type: 'number', +                    exclusiveMaximum: 0 +                }, +                inputs: [ +                    {expected: true,  value: -1}, +                    {expected: false, value: 0}, +                    {expected: false, value: 1} +                ] +            }, + +            // Integer limits tests +            { +                schema: { +                    type: 'integer', +                    minimum: 0 +                }, +                inputs: [ +                    {expected: false, value: -1}, +                    {expected: true,  value: 0}, +                    {expected: true,  value: 1} +                ] +            }, +            { +                schema: { +                    type: 'integer', +                    exclusiveMinimum: 0 +                }, +                inputs: [ +                    {expected: false, value: -1}, +                    {expected: false, value: 0}, +                    {expected: true,  value: 1} +                ] +            }, +            { +                schema: { +                    type: 'integer', +                    maximum: 0 +                }, +                inputs: [ +                    {expected: true,  value: -1}, +                    {expected: true,  value: 0}, +                    {expected: false, value: 1} +                ] +            }, +            { +                schema: { +                    type: 'integer', +                    exclusiveMaximum: 0 +                }, +                inputs: [ +                    {expected: true,  value: -1}, +                    {expected: false, value: 0}, +                    {expected: false, value: 1} +                ] +            }, +            { +                schema: { +                    type: 'integer', +                    multipleOf: 2 +                }, +                inputs: [ +                    {expected: true,  value: -2}, +                    {expected: false, value: -1}, +                    {expected: true,  value: 0}, +                    {expected: false, value: 1}, +                    {expected: true,  value: 2} +                ] +            }, + +            // Numeric type tests +            { +                schema: { +                    type: 'number' +                }, +                inputs: [ +                    {expected: true,  value: 0}, +                    {expected: true,  value: 0.5}, +                    {expected: true,  value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: false, value: {}} +                ] +            }, +            { +                schema: { +                    type: 'integer' +                }, +                inputs: [ +                    {expected: true,  value: 0}, +                    {expected: false, value: 0.5}, +                    {expected: true,  value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: false, value: {}} +                ] +            }, + +            // Reference tests +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'number' +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    {expected: true,  value: 0}, +                    {expected: true,  value: 0.5}, +                    {expected: true,  value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: false, value: {}} +                ] +            }, +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'integer' +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    {expected: true,  value: 0}, +                    {expected: false, value: 0.5}, +                    {expected: true,  value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: false, value: {}} +                ] +            }, +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    {expected: false, value: 0}, +                    {expected: false, value: 0.5}, +                    {expected: false, value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: true,  value: {}}, +                    {expected: false, value: {test: 0}}, +                    {expected: false, value: {test: 0.5}}, +                    {expected: false, value: {test: 1}}, +                    {expected: false, value: {test: '0'}}, +                    {expected: false, value: {test: null}}, +                    {expected: false, value: {test: []}}, +                    {expected: true,  value: {test: {}}}, +                    {expected: true,  value: {test: {test: {}}}}, +                    {expected: true,  value: {test: {test: {test: {}}}}} +                ] +            } +        ]; + +        for (const {schema, inputs} of data) { +            for (const {expected, value} of inputs) { +                const actual = schemaValidate(schema, value); +                expect(actual).toStrictEqual(expected); +            } +        } +    }); +} + + +function testGetValidValueOrDefault1() { +    test('GetValidValueOrDefault1', () => { +        const data = [ +        // Test value defaulting on objects with additionalProperties=false +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    }, +                    additionalProperties: false +                }, +                inputs: [ +                    [ +                        void 0, +                        {test: 'default'} +                    ], +                    [ +                        null, +                        {test: 'default'} +                    ], +                    [ +                        0, +                        {test: 'default'} +                    ], +                    [ +                        '', +                        {test: 'default'} +                    ], +                    [ +                        [], +                        {test: 'default'} +                    ], +                    [ +                        {}, +                        {test: 'default'} +                    ], +                    [ +                        {test: 'value'}, +                        {test: 'value'} +                    ], +                    [ +                        {test2: 'value2'}, +                        {test: 'default'} +                    ], +                    [ +                        {test: 'value', test2: 'value2'}, +                        {test: 'value'} +                    ] +                ] +            }, + +            // Test value defaulting on objects with additionalProperties=true +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    }, +                    additionalProperties: true +                }, +                inputs: [ +                    [ +                        {}, +                        {test: 'default'} +                    ], +                    [ +                        {test: 'value'}, +                        {test: 'value'} +                    ], +                    [ +                        {test2: 'value2'}, +                        {test: 'default', test2: 'value2'} +                    ], +                    [ +                        {test: 'value', test2: 'value2'}, +                        {test: 'value', test2: 'value2'} +                    ] +                ] +            }, + +            // Test value defaulting on objects with additionalProperties={schema} +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    }, +                    additionalProperties: { +                        type: 'number', +                        default: 10 +                    } +                }, +                inputs: [ +                    [ +                        {}, +                        {test: 'default'} +                    ], +                    [ +                        {test: 'value'}, +                        {test: 'value'} +                    ], +                    [ +                        {test2: 'value2'}, +                        {test: 'default', test2: 10} +                    ], +                    [ +                        {test: 'value', test2: 'value2'}, +                        {test: 'value', test2: 10} +                    ], +                    [ +                        {test2: 2}, +                        {test: 'default', test2: 2} +                    ], +                    [ +                        {test: 'value', test2: 2}, +                        {test: 'value', test2: 2} +                    ], +                    [ +                        {test: 'value', test2: 2, test3: null}, +                        {test: 'value', test2: 2, test3: 10} +                    ], +                    [ +                        {test: 'value', test2: 2, test3: void 0}, +                        {test: 'value', test2: 2, test3: 10} +                    ] +                ] +            }, + +            // Test value defaulting where hasOwnProperty is false +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    } +                }, +                inputs: [ +                    [ +                        {}, +                        {test: 'default'} +                    ], +                    [ +                        {test: 'value'}, +                        {test: 'value'} +                    ], +                    [ +                        Object.create({test: 'value'}), +                        {test: 'default'} +                    ] +                ] +            }, +            { +                schema: { +                    type: 'object', +                    required: ['toString'], +                    properties: { +                        toString: { +                            type: 'string', +                            default: 'default' +                        } +                    } +                }, +                inputs: [ +                    [ +                        {}, +                        {toString: 'default'} +                    ], +                    [ +                        {toString: 'value'}, +                        {toString: 'value'} +                    ], +                    [ +                        Object.create({toString: 'value'}), +                        {toString: 'default'} +                    ] +                ] +            }, + +            // Test enum +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'string', +                            default: 'value1', +                            enum: ['value1', 'value2', 'value3'] +                        } +                    } +                }, +                inputs: [ +                    [ +                        {test: 'value1'}, +                        {test: 'value1'} +                    ], +                    [ +                        {test: 'value2'}, +                        {test: 'value2'} +                    ], +                    [ +                        {test: 'value3'}, +                        {test: 'value3'} +                    ], +                    [ +                        {test: 'value4'}, +                        {test: 'value1'} +                    ] +                ] +            }, + +            // Test valid vs invalid default +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'integer', +                            default: 2, +                            minimum: 1 +                        } +                    } +                }, +                inputs: [ +                    [ +                        {test: -1}, +                        {test: 2} +                    ] +                ] +            }, +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    properties: { +                        test: { +                            type: 'integer', +                            default: 1, +                            minimum: 2 +                        } +                    } +                }, +                inputs: [ +                    [ +                        {test: -1}, +                        {test: -1} +                    ] +                ] +            }, + +            // Test references +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'number', +                            default: 0 +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    [ +                        1, +                        1 +                    ], +                    [ +                        null, +                        0 +                    ], +                    [ +                        'test', +                        0 +                    ], +                    [ +                        {test: 'value'}, +                        0 +                    ] +                ] +            }, +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    [ +                        1, +                        {} +                    ], +                    [ +                        null, +                        {} +                    ], +                    [ +                        'test', +                        {} +                    ], +                    [ +                        {}, +                        {} +                    ], +                    [ +                        {test: {}}, +                        {test: {}} +                    ], +                    [ +                        {test: 'value'}, +                        {test: {}} +                    ], +                    [ +                        {test: {test: {}}}, +                        {test: {test: {}}} +                    ] +                ] +            } +        ]; + +        for (const {schema, inputs} of data) { +            for (const [value, expected] of inputs) { +                const actual = getValidValueOrDefault(schema, value); +                expect(actual).toStrictEqual(expected); +            } +        } +    }); +} + + +function testProxy1() { +    test('Proxy1', () => { +        const data = [ +        // Object tests +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    additionalProperties: false, +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    } +                }, +                tests: [ +                    {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, +                    {error: true,  value: {test: 'default'}, action: (value) => { value.test = null; }}, +                    {error: true,  value: {test: 'default'}, action: (value) => { delete value.test; }}, +                    {error: true,  value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, +                    {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} +                ] +            }, +            { +                schema: { +                    type: 'object', +                    required: ['test'], +                    additionalProperties: true, +                    properties: { +                        test: { +                            type: 'string', +                            default: 'default' +                        } +                    } +                }, +                tests: [ +                    {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, +                    {error: true,  value: {test: 'default'}, action: (value) => { value.test = null; }}, +                    {error: true,  value: {test: 'default'}, action: (value) => { delete value.test; }}, +                    {error: false, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, +                    {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} +                ] +            }, +            { +                schema: { +                    type: 'object', +                    required: ['test1'], +                    additionalProperties: false, +                    properties: { +                        test1: { +                            type: 'object', +                            required: ['test2'], +                            additionalProperties: false, +                            properties: { +                                test2: { +                                    type: 'object', +                                    required: ['test3'], +                                    additionalProperties: false, +                                    properties: { +                                        test3: { +                                            type: 'string', +                                            default: 'default' +                                        } +                                    } +                                } +                            } +                        } +                    } +                }, +                tests: [ +                    {error: false, action: (value) => { value.test1.test2.test3 = 'string'; }}, +                    {error: true,  action: (value) => { value.test1.test2.test3 = null; }}, +                    {error: true,  action: (value) => { delete value.test1.test2.test3; }}, +                    {error: true,  action: (value) => { value.test1.test2 = null; }}, +                    {error: true,  action: (value) => { value.test1 = null; }}, +                    {error: true,  action: (value) => { value.test4 = 'string'; }}, +                    {error: false, action: (value) => { delete value.test4; }} +                ] +            }, + +            // Array tests +            { +                schema: { +                    type: 'array', +                    items: { +                        type: 'string', +                        default: 'default' +                    } +                }, +                tests: [ +                    {error: false, value: ['default'], action: (value) => { value[0] = 'string'; }}, +                    {error: true,  value: ['default'], action: (value) => { value[0] = null; }}, +                    {error: false, value: ['default'], action: (value) => { delete value[0]; }}, +                    {error: false, value: ['default'], action: (value) => { value[1] = 'string'; }}, +                    {error: false, value: ['default'], action: (value) => { +                        value[1] = 'string'; +                        if (value.length !== 2) { throw new Error(`Invalid length; expected=2; actual=${value.length}`); } +                        if (typeof value.push !== 'function') { throw new Error(`Invalid push; expected=function; actual=${typeof value.push}`); } +                    }} +                ] +            }, + +            // Reference tests +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                tests: [ +                    {error: false, value: {}, action: (value) => { value.test = {}; }}, +                    {error: false, value: {}, action: (value) => { value.test = {}; value.test.test = {}; }}, +                    {error: false, value: {}, action: (value) => { value.test = {test: {}}; }}, +                    {error: true,  value: {}, action: (value) => { value.test = null; }}, +                    {error: true,  value: {}, action: (value) => { value.test = 'string'; }}, +                    {error: true,  value: {}, action: (value) => { value.test = {}; value.test.test = 'string'; }}, +                    {error: true,  value: {}, action: (value) => { value.test = {test: 'string'}; }} +                ] +            } +        ]; + +        for (const {schema, tests} of data) { +            for (let {error, value, action} of tests) { +                if (typeof value === 'undefined') { value = getValidValueOrDefault(schema, void 0); } +                value = clone(value); +                expect(schemaValidate(schema, value)).toBe(true); +                const valueProxy = createProxy(schema, value); +                if (error) { +                    expect(() => action(valueProxy)).toThrow(); +                } else { +                    expect(() => action(valueProxy)).not.toThrow(); +                } +            } +        } +    }); +} + + +function main() { +    testValidate1(); +    testValidate2(); +    testGetValidValueOrDefault1(); +    testProxy1(); +} + + +main(); diff --git a/test/object-property-accessor.test.js b/test/object-property-accessor.test.js new file mode 100644 index 00000000..a8730093 --- /dev/null +++ b/test/object-property-accessor.test.js @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {ObjectPropertyAccessor} from '../ext/js/general/object-property-accessor.js'; + +function createTestObject() { +    return { +        0: null, +        value1: { +            value2: {}, +            value3: [], +            value4: null +        }, +        value5: [ +            {}, +            [], +            null +        ] +    }; +} + + +function testGet1() { +    test('Get1', () => { +        const data = [ +            [[], (object) => object], +            [['0'], (object) => object['0']], +            [['value1'], (object) => object.value1], +            [['value1', 'value2'], (object) => object.value1.value2], +            [['value1', 'value3'], (object) => object.value1.value3], +            [['value1', 'value4'], (object) => object.value1.value4], +            [['value5'], (object) => object.value5], +            [['value5', 0], (object) => object.value5[0]], +            [['value5', 1], (object) => object.value5[1]], +            [['value5', 2], (object) => object.value5[2]] +        ]; + +        for (const [pathArray, getExpected] of data) { +            const object = createTestObject(); +            const accessor = new ObjectPropertyAccessor(object); +            const expected = getExpected(object); + +            expect(accessor.get(pathArray)).toStrictEqual(expected); +        } +    }); +} + +function testGet2() { +    test('Get2', () => { +        const object = createTestObject(); +        const accessor = new ObjectPropertyAccessor(object); + +        const data = [ +            [[0], 'Invalid path: [0]'], +            [['0', 'invalid'], 'Invalid path: ["0"].invalid'], +            [['invalid'], 'Invalid path: invalid'], +            [['value1', 'invalid'], 'Invalid path: value1.invalid'], +            [['value1', 'value2', 'invalid'], 'Invalid path: value1.value2.invalid'], +            [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], +            [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], +            [['value1', 'value3', 0], 'Invalid path: value1.value3[0]'], +            [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], +            [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], +            [['value5', 'length'], 'Invalid path: value5.length'], +            [['value5', 0, 'invalid'], 'Invalid path: value5[0].invalid'], +            [['value5', 0, 0], 'Invalid path: value5[0][0]'], +            [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], +            [['value5', 1, 0], 'Invalid path: value5[1][0]'], +            [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], +            [['value5', 2, 0], 'Invalid path: value5[2][0]'], +            [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], +            [['value5', 2.5], 'Invalid index'] +        ]; + +        for (const [pathArray, message] of data) { +            expect(() => accessor.get(pathArray)).toThrow(message); +        } +    }); +} + + +function testSet1() { +    test('Set1', () => { +        const testValue = {}; +        const data = [ +            ['0'], +            ['value1', 'value2'], +            ['value1', 'value3'], +            ['value1', 'value4'], +            ['value1'], +            ['value5', 0], +            ['value5', 1], +            ['value5', 2], +            ['value5'] +        ]; + +        for (const pathArray of data) { +            const object = createTestObject(); +            const accessor = new ObjectPropertyAccessor(object); + +            accessor.set(pathArray, testValue); +            expect(accessor.get(pathArray)).toStrictEqual(testValue); +        } +    }); +} + +function testSet2() { +    test('Set2', () => { +        const object = createTestObject(); +        const accessor = new ObjectPropertyAccessor(object); + +        const testValue = {}; +        const data = [ +            [[], 'Invalid path'], +            [[0], 'Invalid path: [0]'], +            [['0', 'invalid'], 'Invalid path: ["0"].invalid'], +            [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], +            [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], +            [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], +            [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], +            [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], +            [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], +            [['value5', 2, 0], 'Invalid path: value5[2][0]'], +            [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], +            [['value5', 2.5], 'Invalid index'] +        ]; + +        for (const [pathArray, message] of data) { +            expect(() => accessor.set(pathArray, testValue)).toThrow(message); +        } +    }); +} + + +function testDelete1() { +    test('Delete1', () => { +        const hasOwn = (object, property) => Object.prototype.hasOwnProperty.call(object, property); + +        const data = [ +            [['0'], (object) => !hasOwn(object, '0')], +            [['value1', 'value2'], (object) => !hasOwn(object.value1, 'value2')], +            [['value1', 'value3'], (object) => !hasOwn(object.value1, 'value3')], +            [['value1', 'value4'], (object) => !hasOwn(object.value1, 'value4')], +            [['value1'], (object) => !hasOwn(object, 'value1')], +            [['value5'], (object) => !hasOwn(object, 'value5')] +        ]; + +        for (const [pathArray, validate] of data) { +            const object = createTestObject(); +            const accessor = new ObjectPropertyAccessor(object); + +            accessor.delete(pathArray); +            expect(validate(object)).toBe(true); +        } +    }); +} + +function testDelete2() { +    test('Delete2', () => { +        const data = [ +            [[], 'Invalid path'], +            [[0], 'Invalid path: [0]'], +            [['0', 'invalid'], 'Invalid path: ["0"].invalid'], +            [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], +            [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], +            [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], +            [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], +            [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], +            [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], +            [['value5', 2, 0], 'Invalid path: value5[2][0]'], +            [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], +            [['value5', 2.5], 'Invalid index'], +            [['value5', 0], 'Invalid type'], +            [['value5', 1], 'Invalid type'], +            [['value5', 2], 'Invalid type'] +        ]; + +        for (const [pathArray, message] of data) { +            const object = createTestObject(); +            const accessor = new ObjectPropertyAccessor(object); + +            expect(() => accessor.delete(pathArray)).toThrow(message); +        } +    }); +} + + +function testSwap1() { +    test('Swap1', () => { +        const data = [ +            [['0'], true], +            [['value1', 'value2'], true], +            [['value1', 'value3'], true], +            [['value1', 'value4'], true], +            [['value1'], false], +            [['value5', 0], true], +            [['value5', 1], true], +            [['value5', 2], true], +            [['value5'], false] +        ]; + +        for (const [pathArray1, compareValues1] of data) { +            for (const [pathArray2, compareValues2] of data) { +                const object = createTestObject(); +                const accessor = new ObjectPropertyAccessor(object); + +                const value1a = accessor.get(pathArray1); +                const value2a = accessor.get(pathArray2); + +                accessor.swap(pathArray1, pathArray2); + +                if (!compareValues1 || !compareValues2) { continue; } + +                const value1b = accessor.get(pathArray1); +                const value2b = accessor.get(pathArray2); + +                expect(value1a).toStrictEqual(value2b); +                expect(value2a).toStrictEqual(value1b); +            } +        } +    }); +} + +function testSwap2() { +    test('Swap2', () => { +        const data = [ +            [[], [], false, 'Invalid path 1'], +            [['0'], [], false, 'Invalid path 2'], +            [[], ['0'], false, 'Invalid path 1'], +            [[0], ['0'], false, 'Invalid path 1: [0]'], +            [['0'], [0], false, 'Invalid path 2: [0]'] +        ]; + +        for (const [pathArray1, pathArray2, checkRevert, message] of data) { +            const object = createTestObject(); +            const accessor = new ObjectPropertyAccessor(object); + +            let value1a; +            let value2a; +            if (checkRevert) { +                try { +                    value1a = accessor.get(pathArray1); +                    value2a = accessor.get(pathArray2); +                } catch (e) { +                // NOP +                } +            } + +            expect(() => accessor.swap(pathArray1, pathArray2)).toThrow(message); + +            if (!checkRevert) { continue; } + +            const value1b = accessor.get(pathArray1); +            const value2b = accessor.get(pathArray2); + +            expect(value1a).toStrictEqual(value1b); +            expect(value2a).toStrictEqual(value2b); +        } +    }); +} + + +function testGetPathString1() { +    test('GetPathString1', () => { +        const data = [ +            [[], ''], +            [[0], '[0]'], +            [['escape\\'], '["escape\\\\"]'], +            [['\'quote\''], '["\'quote\'"]'], +            [['"quote"'], '["\\"quote\\""]'], +            [['part1', 'part2'], 'part1.part2'], +            [['part1', 'part2', 3], 'part1.part2[3]'], +            [['part1', 'part2', '3'], 'part1.part2["3"]'], +            [['part1', 'part2', '3part'], 'part1.part2["3part"]'], +            [['part1', 'part2', '3part', 'part4'], 'part1.part2["3part"].part4'], +            [['part1', 'part2', '3part', '4part'], 'part1.part2["3part"]["4part"]'] +        ]; + +        for (const [pathArray, expected] of data) { +            expect(ObjectPropertyAccessor.getPathString(pathArray)).toStrictEqual(expected); +        } +    }); +} + +function testGetPathString2() { +    test('GetPathString2', () => { +        const data = [ +            [[1.5], 'Invalid index'], +            [[null], 'Invalid type: object'] +        ]; + +        for (const [pathArray, message] of data) { +            expect(() => ObjectPropertyAccessor.getPathString(pathArray)).toThrow(message); +        } +    }); +} + + +function testGetPathArray1() { +    test('GetPathArray1', () => { +        const data = [ +            ['', []], +            ['[0]', [0]], +            ['["escape\\\\"]', ['escape\\']], +            ['["\'quote\'"]', ['\'quote\'']], +            ['["\\"quote\\""]', ['"quote"']], +            ['part1.part2', ['part1', 'part2']], +            ['part1.part2[3]', ['part1', 'part2', 3]], +            ['part1.part2["3"]', ['part1', 'part2', '3']], +            ['part1.part2[\'3\']', ['part1', 'part2', '3']], +            ['part1.part2["3part"]', ['part1', 'part2', '3part']], +            ['part1.part2[\'3part\']', ['part1', 'part2', '3part']], +            ['part1.part2["3part"].part4', ['part1', 'part2', '3part', 'part4']], +            ['part1.part2[\'3part\'].part4', ['part1', 'part2', '3part', 'part4']], +            ['part1.part2["3part"]["4part"]', ['part1', 'part2', '3part', '4part']], +            ['part1.part2[\'3part\'][\'4part\']', ['part1', 'part2', '3part', '4part']] +        ]; + +        for (const [pathString, expected] of data) { +            expect(ObjectPropertyAccessor.getPathArray(pathString)).toStrictEqual(expected); +        } +    }); +} + +function testGetPathArray2() { +    test('GetPathArray2', () => { +        const data = [ +            ['?', 'Unexpected character: ?'], +            ['.', 'Unexpected character: .'], +            ['0', 'Unexpected character: 0'], +            ['part1.[0]', 'Unexpected character: ['], +            ['part1?', 'Unexpected character: ?'], +            ['[part1]', 'Unexpected character: p'], +            ['[0a]', 'Unexpected character: a'], +            ['["part1"x]', 'Unexpected character: x'], +            ['[\'part1\'x]', 'Unexpected character: x'], +            ['["part1"]x', 'Unexpected character: x'], +            ['[\'part1\']x', 'Unexpected character: x'], +            ['part1..part2', 'Unexpected character: .'], + +            ['[', 'Path not terminated correctly'], +            ['part1.', 'Path not terminated correctly'], +            ['part1[', 'Path not terminated correctly'], +            ['part1["', 'Path not terminated correctly'], +            ['part1[\'', 'Path not terminated correctly'], +            ['part1[""', 'Path not terminated correctly'], +            ['part1[\'\'', 'Path not terminated correctly'], +            ['part1[0', 'Path not terminated correctly'], +            ['part1[0].', 'Path not terminated correctly'] +        ]; + +        for (const [pathString, message] of data) { +            expect(() => ObjectPropertyAccessor.getPathArray(pathString)).toThrow(message); +        } +    }); +} + + +function testHasProperty() { +    test('HasProperty', () => { +        const data = [ +            [{}, 'invalid', false], +            [{}, 0, false], +            [{valid: 0}, 'valid', true], +            [{null: 0}, null, false], +            [[], 'invalid', false], +            [[], 0, false], +            [[0], 0, true], +            [[0], null, false], +            ['string', 0, false], +            ['string', 'length', false], +            ['string', null, false] +        ]; + +        for (const [object, property, expected] of data) { +            expect(ObjectPropertyAccessor.hasProperty(object, property)).toStrictEqual(expected); +        } +    }); +} + +function testIsValidPropertyType() { +    test('IsValidPropertyType', () => { +        const data = [ +            [{}, 'invalid', true], +            [{}, 0, false], +            [{valid: 0}, 'valid', true], +            [{null: 0}, null, false], +            [[], 'invalid', false], +            [[], 0, true], +            [[0], 0, true], +            [[0], null, false], +            ['string', 0, false], +            ['string', 'length', false], +            ['string', null, false] +        ]; + +        for (const [object, property, expected] of data) { +            expect(ObjectPropertyAccessor.isValidPropertyType(object, property)).toStrictEqual(expected); +        } +    }); +} + + +function main() { +    testGet1(); +    testGet2(); +    testSet1(); +    testSet2(); +    testDelete1(); +    testDelete2(); +    testSwap1(); +    testSwap2(); +    testGetPathString1(); +    testGetPathString2(); +    testGetPathArray1(); +    testGetPathArray2(); +    testHasProperty(); +    testIsValidPropertyType(); +} + + +main(); diff --git a/test/test-options-util.js b/test/options-util.test.js index d94028c0..9f49eb28 100644 --- a/test/test-options-util.js +++ b/test/options-util.test.js @@ -16,53 +16,33 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -const fs = require('fs'); -const url = require('url'); -const path = require('path'); -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - - -function createVM(extDir) { -    const chrome = { -        runtime: { -            getURL(path2) { -                return url.pathToFileURL(path.join(extDir, path2.replace(/^\//, ''))).href; -            } -        } +import fs from 'fs'; +import url, {fileURLToPath} from 'node:url'; +import path from 'path'; +import {expect, test, vi} from 'vitest'; +import {OptionsUtil} from '../ext/js/data/options-util.js'; +import {TemplatePatcher} from '../ext/js/templates/template-patcher.js'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +vi.stubGlobal('fetch', async function fetch(url2) { +    const filePath = url.fileURLToPath(url2); +    await Promise.resolve(); +    const content = fs.readFileSync(filePath, {encoding: null}); +    return { +        ok: true, +        status: 200, +        statusText: 'OK', +        text: async () => Promise.resolve(content.toString('utf8')), +        json: async () => Promise.resolve(JSON.parse(content.toString('utf8')))      }; - -    async function fetch(url2) { -        const filePath = url.fileURLToPath(url2); -        await Promise.resolve(); -        const content = fs.readFileSync(filePath, {encoding: null}); -        return { -            ok: true, -            status: 200, -            statusText: 'OK', -            text: async () => Promise.resolve(content.toString('utf8')), -            json: async () => Promise.resolve(JSON.parse(content.toString('utf8'))) -        }; +}); +vi.stubGlobal('chrome', { +    runtime: { +        getURL: (path2) => { +            return url.pathToFileURL(path.join(dirname, '..', 'ext', path2.replace(/^\//, ''))).href; +        }      } - -    const vm = new VM({chrome, fetch}); -    vm.execute([ -        'js/core.js', -        'js/general/cache-map.js', -        'js/data/json-schema.js', -        'js/templates/template-patcher.js', -        'js/data/options-util.js' -    ]); - -    return vm; -} - - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} - +});  function createProfileOptionsTestData1() {      return { @@ -632,131 +612,130 @@ function createOptionsUpdatedTestData1() {  } -async function testUpdate(extDir) { -    const vm = createVM(extDir); -    const [OptionsUtil] = vm.get(['OptionsUtil']); -    const optionsUtil = new OptionsUtil(); -    await optionsUtil.prepare(); +async function testUpdate() { +    test('Update', async () => { +        const optionsUtil = new OptionsUtil(); +        await optionsUtil.prepare(); -    const options = createOptionsTestData1(); -    const optionsUpdated = clone(await optionsUtil.update(options)); -    const optionsExpected = createOptionsUpdatedTestData1(); -    assert.deepStrictEqual(optionsUpdated, optionsExpected); +        const options = createOptionsTestData1(); +        const optionsUpdated = structuredClone(await optionsUtil.update(options)); +        const optionsExpected = createOptionsUpdatedTestData1(); +        expect(optionsUpdated).toStrictEqual(optionsExpected); +    });  } -async function testDefault(extDir) { -    const data = [ -        (options) => options, -        (options) => { -            delete options.profiles[0].options.audio.autoPlay; -        }, -        (options) => { -            options.profiles[0].options.audio.autoPlay = void 0; -        } -    ]; +async function testDefault() { +    test('Default', async () => { +        const data = [ +            (options) => options, +            (options) => { +                delete options.profiles[0].options.audio.autoPlay; +            }, +            (options) => { +                options.profiles[0].options.audio.autoPlay = void 0; +            } +        ]; -    const vm = createVM(extDir); -    const [OptionsUtil] = vm.get(['OptionsUtil']); -    const optionsUtil = new OptionsUtil(); -    await optionsUtil.prepare(); +        const optionsUtil = new OptionsUtil(); +        await optionsUtil.prepare(); -    for (const modify of data) { -        const options = optionsUtil.getDefault(); +        for (const modify of data) { +            const options = optionsUtil.getDefault(); -        const optionsModified = clone(options); -        modify(optionsModified); +            const optionsModified = structuredClone(options); +            modify(optionsModified); -        const optionsUpdated = await optionsUtil.update(clone(optionsModified)); -        assert.deepStrictEqual(clone(optionsUpdated), clone(options)); -    } +            const optionsUpdated = await optionsUtil.update(structuredClone(optionsModified)); +            expect(structuredClone(optionsUpdated)).toStrictEqual(structuredClone(options)); +        } +    });  } -async function testFieldTemplatesUpdate(extDir) { -    const vm = createVM(extDir); -    const [OptionsUtil, TemplatePatcher] = vm.get(['OptionsUtil', 'TemplatePatcher']); -    const optionsUtil = new OptionsUtil(); -    await optionsUtil.prepare(); +async function testFieldTemplatesUpdate() { +    test('FieldTemplatesUpdate', async () => { +        const optionsUtil = new OptionsUtil(); +        await optionsUtil.prepare(); -    const templatePatcher = new TemplatePatcher(); -    const loadDataFile = (fileName) => { -        const content = fs.readFileSync(path.join(extDir, fileName), {encoding: 'utf8'}); -        return templatePatcher.parsePatch(content).addition; -    }; -    const updates = [ -        {version: 2,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v2.handlebars')}, -        {version: 4,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v4.handlebars')}, -        {version: 6,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v6.handlebars')}, -        {version: 8,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v8.handlebars')}, -        {version: 10, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v10.handlebars')}, -        {version: 12, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v12.handlebars')}, -        {version: 13, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v13.handlebars')}, -        {version: 21, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v21.handlebars')} -    ]; -    const getUpdateAdditions = (startVersion, targetVersion) => { -        let value = ''; -        for (const {version, changes} of updates) { -            if (version <= startVersion || version > targetVersion || changes.length === 0) { continue; } -            if (value.length > 0) { value += '\n'; } -            value += changes; -        } -        return value; -    }; +        const templatePatcher = new TemplatePatcher(); +        const loadDataFile = (fileName) => { +            const content = fs.readFileSync(path.join(dirname, '..', 'ext', fileName), {encoding: 'utf8'}); +            return templatePatcher.parsePatch(content).addition; +        }; +        const updates = [ +            {version: 2,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v2.handlebars')}, +            {version: 4,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v4.handlebars')}, +            {version: 6,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v6.handlebars')}, +            {version: 8,  changes: loadDataFile('data/templates/anki-field-templates-upgrade-v8.handlebars')}, +            {version: 10, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v10.handlebars')}, +            {version: 12, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v12.handlebars')}, +            {version: 13, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v13.handlebars')}, +            {version: 21, changes: loadDataFile('data/templates/anki-field-templates-upgrade-v21.handlebars')} +        ]; +        const getUpdateAdditions = (startVersion, targetVersion) => { +            let value = ''; +            for (const {version, changes} of updates) { +                if (version <= startVersion || version > targetVersion || changes.length === 0) { continue; } +                if (value.length > 0) { value += '\n'; } +                value += changes; +            } +            return value; +        }; -    const data = [ +        const data = [          // Standard format -        { -            oldVersion: 0, -            newVersion: 12, -            old: ` +            { +                oldVersion: 0, +                newVersion: 12, +                old: `  {{#*inline "character"}}      {{~definition.character~}}  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "character"}}      {{~definition.character~}}  {{/inline}}  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // Non-standard marker format -        { -            oldVersion: 0, -            newVersion: 12, -            old: ` +            }, +            // Non-standard marker format +            { +                oldVersion: 0, +                newVersion: 12, +                old: `  {{#*inline "character"}}      {{~definition.character~}}  {{/inline}}  {{~> (lookup . "marker2") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "character"}}      {{~definition.character~}}  {{/inline}}  {{~> (lookup . "marker2") ~}}  <<<UPDATE-ADDITIONS>>>`.trimStart() -        }, -        // Empty test -        { -            oldVersion: 0, -            newVersion: 12, -            old: ` +            }, +            // Empty test +            { +                oldVersion: 0, +                newVersion: 12, +                old: `  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // Definition tags update -        { -            oldVersion: 0, -            newVersion: 12, -            old: ` +            }, +            // Definition tags update +            { +                oldVersion: 0, +                newVersion: 12, +                old: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}} @@ -779,7 +758,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}  `.trimStart(), -            expected: ` +                expected: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#scope~}} @@ -824,12 +803,12 @@ async function testFieldTemplatesUpdate(extDir) {  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}  `.trimStart() -        }, -        // glossary and glossary-brief update -        { -            oldVersion: 7, -            newVersion: 12, -            old: ` +            }, +            // glossary and glossary-brief update +            { +                oldVersion: 7, +                newVersion: 12, +                old: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#scope~}} @@ -895,7 +874,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#scope~}} @@ -964,12 +943,12 @@ async function testFieldTemplatesUpdate(extDir) {  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // formatGlossary update -        { -            oldVersion: 12, -            newVersion: 13, -            old: ` +            }, +            // formatGlossary update +            { +                oldVersion: 12, +                newVersion: 13, +                old: `  {{#*inline "example"}}      {{~#if (op "<=" glossary.length 1)~}}          {{#each glossary}}{{#multiLine}}{{.}}{{/multiLine}}{{/each}} @@ -982,7 +961,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "example"}}      {{~#if (op "<=" glossary.length 1)~}}          {{#each glossary}}{{#formatGlossary ../dictionary}}{{{.}}}{{/formatGlossary}}{{/each}} @@ -995,12 +974,12 @@ async function testFieldTemplatesUpdate(extDir) {  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // hasMedia/getMedia update -        { -            oldVersion: 12, -            newVersion: 13, -            old: ` +            }, +            // hasMedia/getMedia update +            { +                oldVersion: 12, +                newVersion: 13, +                old: `  {{#*inline "audio"}}      {{~#if definition.audioFileName~}}          [sound:{{definition.audioFileName}}] @@ -1023,7 +1002,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "audio"}}      {{~#if (hasMedia "audio")~}}          [sound:{{#getMedia "audio"}}{{/getMedia}}] @@ -1048,12 +1027,12 @@ async function testFieldTemplatesUpdate(extDir) {  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // hasMedia/getMedia update -        { -            oldVersion: 12, -            newVersion: 13, -            old: ` +            }, +            // hasMedia/getMedia update +            { +                oldVersion: 12, +                newVersion: 13, +                old: `  {{! Pitch Accents }}  {{#*inline "pitch-accent-item-downstep-notation"}}      {{~#scope~}} @@ -1166,7 +1145,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{! Pitch Accents }}  {{#*inline "pitch-accent-item"}}      {{~#pronunciation format=format reading=reading downstepPosition=position nasalPositions=nasalPositions devoicePositions=devoicePositions~}}{{~/pronunciation~}} @@ -1216,12 +1195,12 @@ async function testFieldTemplatesUpdate(extDir) {  <<<UPDATE-ADDITIONS>>>  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // block helper update: furigana and furiganaPlain -        { -            oldVersion: 20, -            newVersion: 21, -            old: ` +            }, +            // block helper update: furigana and furiganaPlain +            { +                oldVersion: 20, +                newVersion: 21, +                old: `  {{#*inline "furigana"}}      {{~#if merge~}}          {{~#each definition.expressions~}} @@ -1263,7 +1242,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "furigana"}}      {{~#if merge~}}          {{~#each definition.expressions~}} @@ -1304,12 +1283,12 @@ async function testFieldTemplatesUpdate(extDir) {  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // block helper update: formatGlossary -        { -            oldVersion: 20, -            newVersion: 21, -            old: ` +            }, +            // block helper update: formatGlossary +            { +                oldVersion: 20, +                newVersion: 21, +                old: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#scope~}} @@ -1344,7 +1323,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "glossary-single"}}      {{~#unless brief~}}          {{~#scope~}} @@ -1378,12 +1357,12 @@ async function testFieldTemplatesUpdate(extDir) {  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // block helper update: set and get -        { -            oldVersion: 20, -            newVersion: 21, -            old: ` +            }, +            // block helper update: set and get +            { +                oldVersion: 20, +                newVersion: 21, +                old: `  {{#*inline "pitch-accent-item-disambiguation"}}      {{~#scope~}}          {{~#set "exclusive" (spread exclusiveExpressions exclusiveReadings)}}{{/set~}} @@ -1432,7 +1411,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "pitch-accent-item-disambiguation"}}      {{~#scope~}}          {{~set "exclusive" (spread exclusiveExpressions exclusiveReadings)~}} @@ -1480,12 +1459,12 @@ async function testFieldTemplatesUpdate(extDir) {  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // block helper update: hasMedia and getMedia -        { -            oldVersion: 20, -            newVersion: 21, -            old: ` +            }, +            // block helper update: hasMedia and getMedia +            { +                oldVersion: 20, +                newVersion: 21, +                old: `  {{#*inline "audio"}}      {{~#if (hasMedia "audio")~}}          [sound:{{#getMedia "audio"}}{{/getMedia}}] @@ -1524,7 +1503,7 @@ async function testFieldTemplatesUpdate(extDir) {  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "audio"}}      {{~#if (hasMedia "audio")~}}          [sound:{{getMedia "audio"}}] @@ -1562,48 +1541,47 @@ async function testFieldTemplatesUpdate(extDir) {  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart() -        }, -        // block helper update: pronunciation -        { -            oldVersion: 20, -            newVersion: 21, -            old: ` +            }, +            // block helper update: pronunciation +            { +                oldVersion: 20, +                newVersion: 21, +                old: `  {{#*inline "pitch-accent-item"}}      {{~#pronunciation format=format reading=reading downstepPosition=position nasalPositions=nasalPositions devoicePositions=devoicePositions~}}{{~/pronunciation~}}  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart(), -            expected: ` +                expected: `  {{#*inline "pitch-accent-item"}}      {{~pronunciation format=format reading=reading downstepPosition=position nasalPositions=nasalPositions devoicePositions=devoicePositions~}}  {{/inline}}  {{~> (lookup . "marker") ~}}`.trimStart() -        } -    ]; +            } +        ]; -    const updatesPattern = /<<<UPDATE-ADDITIONS>>>/g; -    for (const {old, expected, oldVersion, newVersion} of data) { -        const options = createOptionsTestData1(); -        options.profiles[0].options.anki.fieldTemplates = old; -        options.version = oldVersion; +        const updatesPattern = /<<<UPDATE-ADDITIONS>>>/g; +        for (const {old, expected, oldVersion, newVersion} of data) { +            const options = createOptionsTestData1(); +            options.profiles[0].options.anki.fieldTemplates = old; +            options.version = oldVersion; -        const expected2 = expected.replace(updatesPattern, getUpdateAdditions(oldVersion, newVersion)); +            const expected2 = expected.replace(updatesPattern, getUpdateAdditions(oldVersion, newVersion)); -        const optionsUpdated = clone(await optionsUtil.update(options, newVersion)); -        const fieldTemplatesActual = optionsUpdated.profiles[0].options.anki.fieldTemplates; -        assert.deepStrictEqual(fieldTemplatesActual, expected2); -    } +            const optionsUpdated = structuredClone(await optionsUtil.update(options, newVersion)); +            const fieldTemplatesActual = optionsUpdated.profiles[0].options.anki.fieldTemplates; +            expect(fieldTemplatesActual).toStrictEqual(expected2); +        } +    });  }  async function main() { -    const extDir = path.join(__dirname, '..', 'ext'); -    await testUpdate(extDir); -    await testDefault(extDir); -    await testFieldTemplatesUpdate(extDir); +    await testUpdate(); +    await testDefault(); +    await testFieldTemplatesUpdate();  } - -if (require.main === module) { testMain(main); } +await main(); diff --git a/test/profile-conditions-util.test.js b/test/profile-conditions-util.test.js new file mode 100644 index 00000000..ca8b00ef --- /dev/null +++ b/test/profile-conditions-util.test.js @@ -0,0 +1,1090 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {ProfileConditionsUtil} from '../ext/js/background/profile-conditions-util.js'; + +function testNormalizeContext() { +    test('NormalizeContext', () => { +        const data = [ +        // Empty +            { +                context: {}, +                expected: {flags: []} +            }, + +            // Domain normalization +            { +                context: {url: ''}, +                expected: {url: '', flags: []} +            }, +            { +                context: {url: 'http://example.com/'}, +                expected: {url: 'http://example.com/', domain: 'example.com', flags: []} +            }, +            { +                context: {url: 'http://example.com:1234/'}, +                expected: {url: 'http://example.com:1234/', domain: 'example.com', flags: []} +            }, +            { +                context: {url: 'http://user@example.com:1234/'}, +                expected: {url: 'http://user@example.com:1234/', domain: 'example.com', flags: []} +            } +        ]; + +        for (const {context, expected} of data) { +            const profileConditionsUtil = new ProfileConditionsUtil(); +            const actual = profileConditionsUtil.normalizeContext(context); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testSchemas() { +    test('Schemas', () => { +        const data = [ +        // Empty +            { +                conditionGroups: [], +                expectedSchema: {}, +                inputs: [ +                    {expected: true, context: {url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    {conditions: []} +                ], +                expectedSchema: {}, +                inputs: [ +                    {expected: true, context: {url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    {conditions: []}, +                    {conditions: []} +                ], +                expectedSchema: {}, +                inputs: [ +                    {expected: true, context: {url: 'http://example.com/'}} +                ] +            }, + +            // popupLevel tests +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'equal', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        depth: {const: 0} +                    }, +                    required: ['depth'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'notEqual', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    not: [ +                        { +                            properties: { +                                depth: {const: 0} +                            }, +                            required: ['depth'] +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'lessThan', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        depth: { +                            type: 'number', +                            exclusiveMaximum: 0 +                        } +                    }, +                    required: ['depth'] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'greaterThan', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        depth: { +                            type: 'number', +                            exclusiveMinimum: 0 +                        } +                    }, +                    required: ['depth'] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'lessThanOrEqual', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        depth: { +                            type: 'number', +                            maximum: 0 +                        } +                    }, +                    required: ['depth'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'greaterThanOrEqual', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        depth: { +                            type: 'number', +                            minimum: 0 +                        } +                    }, +                    required: ['depth'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: -1, url: 'http://example.com/'}} +                ] +            }, + +            // url tests +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'url', +                                operator: 'matchDomain', +                                value: 'example.com' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        domain: { +                            oneOf: [ +                                {const: 'example.com'} +                            ] +                        } +                    }, +                    required: ['domain'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 0, url: 'http://example1.com/'}}, +                    {expected: false, context: {depth: 0, url: 'http://example2.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com:1234/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://user@example.com:1234/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'url', +                                operator: 'matchDomain', +                                value: 'example.com, example1.com, example2.com' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        domain: { +                            oneOf: [ +                                {const: 'example.com'}, +                                {const: 'example1.com'}, +                                {const: 'example2.com'} +                            ] +                        } +                    }, +                    required: ['domain'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example1.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example2.com/'}}, +                    {expected: false, context: {depth: 0, url: 'http://example3.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com:1234/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://user@example.com:1234/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'url', +                                operator: 'matchRegExp', +                                value: '^http://example\\d?\\.com/[\\w\\W]*$' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        url: { +                            type: 'string', +                            pattern: '^http://example\\d?\\.com/[\\w\\W]*$', +                            patternFlags: 'i' +                        } +                    }, +                    required: ['url'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example1.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example2.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example3.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/example'}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com:1234/'}}, +                    {expected: false, context: {depth: 0, url: 'http://user@example.com:1234/'}}, +                    {expected: false, context: {depth: 0, url: 'http://example-1.com/'}} +                ] +            }, + +            // modifierKeys tests +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'are', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array', +                            maxItems: 0, +                            minItems: 0 +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'are', +                                value: 'Alt, Shift' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array', +                            maxItems: 2, +                            minItems: 2, +                            allOf: [ +                                {contains: {const: 'Alt'}}, +                                {contains: {const: 'Shift'}} +                            ] +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'areNot', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    not: [ +                        { +                            properties: { +                                modifierKeys: { +                                    type: 'array', +                                    maxItems: 0, +                                    minItems: 0 +                                } +                            }, +                            required: ['modifierKeys'] +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'areNot', +                                value: 'Alt, Shift' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    not: [ +                        { +                            properties: { +                                modifierKeys: { +                                    type: 'array', +                                    maxItems: 2, +                                    minItems: 2, +                                    allOf: [ +                                        {contains: {const: 'Alt'}}, +                                        {contains: {const: 'Shift'}} +                                    ] +                                } +                            }, +                            required: ['modifierKeys'] +                        } +                    ] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'include', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array', +                            minItems: 0 +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'include', +                                value: 'Alt, Shift' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array', +                            minItems: 2, +                            allOf: [ +                                {contains: {const: 'Alt'}}, +                                {contains: {const: 'Shift'}} +                            ] +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'notInclude', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array' +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'modifierKeys', +                                operator: 'notInclude', +                                value: 'Alt, Shift' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    properties: { +                        modifierKeys: { +                            type: 'array', +                            not: [ +                                {contains: {const: 'Alt'}}, +                                {contains: {const: 'Shift'}} +                            ] +                        } +                    }, +                    required: ['modifierKeys'] +                }, +                inputs: [ +                    {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} +                ] +            }, + +            // flags tests +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'are', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array', +                            maxItems: 0, +                            minItems: 0 +                        } +                    } +                }, +                inputs: [ +                    {expected: true,  context: {}}, +                    {expected: true,  context: {flags: []}}, +                    {expected: false, context: {flags: ['test1']}}, +                    {expected: false, context: {flags: ['test1', 'test2']}}, +                    {expected: false, context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'are', +                                value: 'test1, test2' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array', +                            maxItems: 2, +                            minItems: 2, +                            allOf: [ +                                {contains: {const: 'test1'}}, +                                {contains: {const: 'test2'}} +                            ] +                        } +                    } +                }, +                inputs: [ +                    {expected: false, context: {}}, +                    {expected: false, context: {flags: []}}, +                    {expected: false, context: {flags: ['test1']}}, +                    {expected: true,  context: {flags: ['test1', 'test2']}}, +                    {expected: false, context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'areNot', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    not: [ +                        { +                            required: ['flags'], +                            properties: { +                                flags: { +                                    type: 'array', +                                    maxItems: 0, +                                    minItems: 0 +                                } +                            } +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {}}, +                    {expected: false, context: {flags: []}}, +                    {expected: true,  context: {flags: ['test1']}}, +                    {expected: true,  context: {flags: ['test1', 'test2']}}, +                    {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'areNot', +                                value: 'test1, test2' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    not: [ +                        { +                            required: ['flags'], +                            properties: { +                                flags: { +                                    type: 'array', +                                    maxItems: 2, +                                    minItems: 2, +                                    allOf: [ +                                        {contains: {const: 'test1'}}, +                                        {contains: {const: 'test2'}} +                                    ] +                                } +                            } +                        } +                    ] +                }, +                inputs: [ +                    {expected: true,  context: {}}, +                    {expected: true,  context: {flags: []}}, +                    {expected: true,  context: {flags: ['test1']}}, +                    {expected: false, context: {flags: ['test1', 'test2']}}, +                    {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'include', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array', +                            minItems: 0 +                        } +                    } +                }, +                inputs: [ +                    {expected: true,  context: {}}, +                    {expected: true,  context: {flags: []}}, +                    {expected: true,  context: {flags: ['test1']}}, +                    {expected: true,  context: {flags: ['test1', 'test2']}}, +                    {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'include', +                                value: 'test1, test2' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array', +                            minItems: 2, +                            allOf: [ +                                {contains: {const: 'test1'}}, +                                {contains: {const: 'test2'}} +                            ] +                        } +                    } +                }, +                inputs: [ +                    {expected: false, context: {}}, +                    {expected: false, context: {flags: []}}, +                    {expected: false, context: {flags: ['test1']}}, +                    {expected: true,  context: {flags: ['test1', 'test2']}}, +                    {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'notInclude', +                                value: '' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array' +                        } +                    } +                }, +                inputs: [ +                    {expected: true,  context: {}}, +                    {expected: true,  context: {flags: []}}, +                    {expected: true,  context: {flags: ['test1']}}, +                    {expected: true,  context: {flags: ['test1', 'test2']}}, +                    {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'flags', +                                operator: 'notInclude', +                                value: 'test1, test2' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    required: ['flags'], +                    properties: { +                        flags: { +                            type: 'array', +                            not: [ +                                {contains: {const: 'test1'}}, +                                {contains: {const: 'test2'}} +                            ] +                        } +                    } +                }, +                inputs: [ +                    {expected: true,  context: {}}, +                    {expected: true,  context: {flags: []}}, +                    {expected: false, context: {flags: ['test1']}}, +                    {expected: false, context: {flags: ['test1', 'test2']}}, +                    {expected: false, context: {flags: ['test1', 'test2', 'test3']}} +                ] +            }, + +            // Multiple conditions tests +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'greaterThan', +                                value: '0' +                            }, +                            { +                                type: 'popupLevel', +                                operator: 'lessThan', +                                value: '3' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    allOf: [ +                        { +                            properties: { +                                depth: { +                                    type: 'number', +                                    exclusiveMinimum: 0 +                                } +                            }, +                            required: ['depth'] +                        }, +                        { +                            properties: { +                                depth: { +                                    type: 'number', +                                    exclusiveMaximum: 3 +                                } +                            }, +                            required: ['depth'] +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {depth: -2, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: -1, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 3, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'greaterThan', +                                value: '0' +                            }, +                            { +                                type: 'popupLevel', +                                operator: 'lessThan', +                                value: '3' +                            } +                        ] +                    }, +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'equal', +                                value: '0' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    anyOf: [ +                        { +                            allOf: [ +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            exclusiveMinimum: 0 +                                        } +                                    }, +                                    required: ['depth'] +                                }, +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            exclusiveMaximum: 3 +                                        } +                                    }, +                                    required: ['depth'] +                                } +                            ] +                        }, +                        { +                            properties: { +                                depth: {const: 0} +                            }, +                            required: ['depth'] +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {depth: -2, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: -1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 3, url: 'http://example.com/'}} +                ] +            }, +            { +                conditionGroups: [ +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'greaterThan', +                                value: '0' +                            }, +                            { +                                type: 'popupLevel', +                                operator: 'lessThan', +                                value: '3' +                            } +                        ] +                    }, +                    { +                        conditions: [ +                            { +                                type: 'popupLevel', +                                operator: 'lessThanOrEqual', +                                value: '0' +                            }, +                            { +                                type: 'popupLevel', +                                operator: 'greaterThanOrEqual', +                                value: '-1' +                            } +                        ] +                    } +                ], +                expectedSchema: { +                    anyOf: [ +                        { +                            allOf: [ +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            exclusiveMinimum: 0 +                                        } +                                    }, +                                    required: ['depth'] +                                }, +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            exclusiveMaximum: 3 +                                        } +                                    }, +                                    required: ['depth'] +                                } +                            ] +                        }, +                        { +                            allOf: [ +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            maximum: 0 +                                        } +                                    }, +                                    required: ['depth'] +                                }, +                                { +                                    properties: { +                                        depth: { +                                            type: 'number', +                                            minimum: -1 +                                        } +                                    }, +                                    required: ['depth'] +                                } +                            ] +                        } +                    ] +                }, +                inputs: [ +                    {expected: false, context: {depth: -2, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: -1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, +                    {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, +                    {expected: false, context: {depth: 3, url: 'http://example.com/'}} +                ] +            } +        ]; + +        for (const {conditionGroups, expectedSchema, inputs} of data) { +            const profileConditionsUtil = new ProfileConditionsUtil(); +            const schema = profileConditionsUtil.createSchema(conditionGroups); +            if (typeof expectedSchema !== 'undefined') { +                expect(schema.schema).toStrictEqual(expectedSchema); +            } +            if (Array.isArray(inputs)) { +                for (const {expected, context} of inputs) { +                    const normalizedContext = profileConditionsUtil.normalizeContext(context); +                    const actual = schema.isValid(normalizedContext); +                    expect(actual).toStrictEqual(expected); +                } +            } +        } +    }); +} + + +function main() { +    testNormalizeContext(); +    testSchemas(); +} + +main(); diff --git a/test/test-all.js b/test/test-all.js deleted file mode 100644 index d187879a..00000000 --- a/test/test-all.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const {spawnSync} = require('child_process'); -const {getArgs} = require('../dev/util'); - - -function main() { -    const args = getArgs(process.argv.slice(2), new Map([ -        ['skip', []], -        [null, []] -    ])); -    const directories = args.get(null); -    const skip = new Set([__filename, ...args.get('skip')].map((value) => path.resolve(value))); - -    const node = process.execPath; -    const fileNamePattern = /\.js$/i; - -    let first = true; -    for (const directory of directories) { -        const fileNames = fs.readdirSync(directory); -        for (const fileName of fileNames) { -            if (!fileNamePattern.test(fileName)) { continue; } - -            const fullFileName = path.resolve(path.join(directory, fileName)); -            if (skip.has(fullFileName)) { continue; } - -            const stats = fs.lstatSync(fullFileName); -            if (!stats.isFile()) { continue; } - -            process.stdout.write(`${first ? '' : '\n'}Running ${fileName}...\n`); -            first = false; - -            const {error, status} = spawnSync(node, [fileName], {cwd: directory, stdio: 'inherit'}); - -            if (status !== null && status !== 0) { -                process.exit(status); -                return; -            } -            if (error) { -                throw error; -            } -        } -    } - -    process.exit(0); -} - - -if (require.main === module) { main(); } diff --git a/test/test-anki-note-builder.js b/test/test-anki-note-builder.js deleted file mode 100644 index ba937302..00000000 --- a/test/test-anki-note-builder.js +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2021-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {JSDOM} = require('jsdom'); -const {testMain} = require('../dev/util'); -const {TranslatorVM} = require('../dev/translator-vm'); - - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} - -async function createVM() { -    const dom = new JSDOM(); -    const {Node, NodeFilter, document} = dom.window; - -    const vm = new TranslatorVM({ -        Node, -        NodeFilter, -        document, -        location: new URL('https://yomichan.test/') -    }); - -    const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', 'valid-dictionary1'); -    await vm.prepare(dictionaryDirectory, 'Test Dictionary 2'); - -    vm.execute([ -        'js/data/anki-note-builder.js', -        'js/data/anki-util.js', -        'js/dom/sandbox/css-style-applier.js', -        'js/display/sandbox/pronunciation-generator.js', -        'js/display/sandbox/structured-content-generator.js', -        'js/templates/sandbox/anki-template-renderer.js', -        'js/templates/sandbox/anki-template-renderer-content-manager.js', -        'js/templates/sandbox/template-renderer.js', -        'js/templates/sandbox/template-renderer-media-provider.js', -        'lib/handlebars.min.js' -    ]); - -    const [ -        JapaneseUtil, -        AnkiNoteBuilder, -        AnkiTemplateRenderer -    ] = vm.get([ -        'JapaneseUtil', -        'AnkiNoteBuilder', -        'AnkiTemplateRenderer' -    ]); - -    class TemplateRendererProxy { -        constructor() { -            this._preparePromise = null; -            this._ankiTemplateRenderer = new AnkiTemplateRenderer(); -        } - -        async render(template, data, type) { -            await this._prepare(); -            return await this._ankiTemplateRenderer.templateRenderer.render(template, data, type); -        } - -        async renderMulti(items) { -            await this._prepare(); -            return await this._serializeMulti(this._ankiTemplateRenderer.templateRenderer.renderMulti(items)); -        } - -        _prepare() { -            if (this._preparePromise === null) { -                this._preparePromise = this._prepareInternal(); -            } -            return this._preparePromise; -        } - -        async _prepareInternal() { -            await this._ankiTemplateRenderer.prepare(); -        } - -        _serializeError(error) { -            try { -                if (typeof error === 'object' && error !== null) { -                    const result = { -                        name: error.name, -                        message: error.message, -                        stack: error.stack -                    }; -                    if (Object.prototype.hasOwnProperty.call(error, 'data')) { -                        result.data = error.data; -                    } -                    return result; -                } -            } catch (e) { -                // NOP -            } -            return { -                value: error, -                hasValue: true -            }; -        } - -        _serializeMulti(array) { -            for (let i = 0, ii = array.length; i < ii; ++i) { -                const value = array[i]; -                const {error} = value; -                if (typeof error !== 'undefined') { -                    value.error = this._serializeError(error); -                } -            } -            return array; -        } -    } -    vm.set({TemplateRendererProxy}); - -    return {vm, AnkiNoteBuilder, JapaneseUtil}; -} - -function getFieldMarkers(type) { -    switch (type) { -        case 'terms': -            return [ -                'audio', -                'clipboard-image', -                'clipboard-text', -                'cloze-body', -                'cloze-prefix', -                'cloze-suffix', -                'conjugation', -                'dictionary', -                'document-title', -                'expression', -                'frequencies', -                'furigana', -                'furigana-plain', -                'glossary', -                'glossary-brief', -                'glossary-no-dictionary', -                'part-of-speech', -                'pitch-accents', -                'pitch-accent-graphs', -                'pitch-accent-positions', -                'reading', -                'screenshot', -                'search-query', -                'selection-text', -                'sentence', -                'sentence-furigana', -                'tags', -                'url' -            ]; -        case 'kanji': -            return [ -                'character', -                'clipboard-image', -                'clipboard-text', -                'cloze-body', -                'cloze-prefix', -                'cloze-suffix', -                'dictionary', -                'document-title', -                'glossary', -                'kunyomi', -                'onyomi', -                'screenshot', -                'search-query', -                'selection-text', -                'sentence', -                'sentence-furigana', -                'stroke-count', -                'tags', -                'url' -            ]; -        default: -            return []; -    } -} - -async function getRenderResults(dictionaryEntries, type, mode, template, AnkiNoteBuilder, JapaneseUtil, write) { -    const markers = getFieldMarkers(type); -    const fields = []; -    for (const marker of markers) { -        fields.push([marker, `{${marker}}`]); -    } - -    const japaneseUtil = new JapaneseUtil(null); -    const clozePrefix = 'cloze-prefix'; -    const clozeSuffix = 'cloze-suffix'; -    const results = []; -    for (const dictionaryEntry of dictionaryEntries) { -        let source = ''; -        switch (dictionaryEntry.type) { -            case 'kanji': -                source = dictionaryEntry.character; -                break; -            case 'term': -                if (dictionaryEntry.headwords.length > 0 && dictionaryEntry.headwords[0].sources.length > 0) { -                    source = dictionaryEntry.headwords[0].sources[0].originalText; -                } -                break; -        } -        const ankiNoteBuilder = new AnkiNoteBuilder({japaneseUtil}); -        const context = { -            url: 'url:', -            sentence: { -                text: `${clozePrefix}${source}${clozeSuffix}`, -                offset: clozePrefix.length -            }, -            documentTitle: 'title', -            query: 'query', -            fullQuery: 'fullQuery' -        }; -        const {note: {fields: noteFields}, errors} = await ankiNoteBuilder.createNote({ -            dictionaryEntry, -            mode: null, -            context, -            template, -            deckName: 'deckName', -            modelName: 'modelName', -            fields, -            tags: ['yomichan'], -            checkForDuplicates: true, -            duplicateScope: 'collection', -            duplicateScopeCheckAllModels: false, -            resultOutputMode: mode, -            glossaryLayoutMode: 'default', -            compactTags: false -        }); -        if (!write) { -            for (const error of errors) { -                console.error(error); -            } -            assert.strictEqual(errors.length, 0); -        } -        results.push(noteFields); -    } - -    return results; -} - - -async function main() { -    const write = (process.argv[2] === '--write'); - -    const {vm, AnkiNoteBuilder, JapaneseUtil} = await createVM(); - -    const testInputsFilePath = path.join(__dirname, 'data', 'translator-test-inputs.json'); -    const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'})); - -    const testResults1FilePath = path.join(__dirname, 'data', 'anki-note-builder-test-results.json'); -    const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'})); -    const actualResults1 = []; - -    const template = fs.readFileSync(path.join(__dirname, '..', 'ext', 'data/templates/default-anki-field-templates.handlebars'), {encoding: 'utf8'}); - -    for (let i = 0, ii = tests.length; i < ii; ++i) { -        const test = tests[i]; -        const expected1 = expectedResults1[i]; -        switch (test.func) { -            case 'findTerms': -                { -                    const {name, mode, text} = test; -                    const options = vm.buildOptions(optionsPresets, test.options); -                    const {dictionaryEntries} = clone(await vm.translator.findTerms(mode, text, options)); -                    const results = mode !== 'simple' ? clone(await getRenderResults(dictionaryEntries, 'terms', mode, template, AnkiNoteBuilder, JapaneseUtil, write)) : null; -                    actualResults1.push({name, results}); -                    if (!write) { -                        assert.deepStrictEqual(results, expected1.results); -                    } -                } -                break; -            case 'findKanji': -                { -                    const {name, text} = test; -                    const options = vm.buildOptions(optionsPresets, test.options); -                    const dictionaryEntries = clone(await vm.translator.findKanji(text, options)); -                    const results = clone(await getRenderResults(dictionaryEntries, 'kanji', null, template, AnkiNoteBuilder, JapaneseUtil, write)); -                    actualResults1.push({name, results}); -                    if (!write) { -                        assert.deepStrictEqual(results, expected1.results); -                    } -                } -                break; -        } -    } - -    if (write) { -        // Use 2 indent instead of 4 to save a bit of file size -        fs.writeFileSync(testResults1FilePath, JSON.stringify(actualResults1, null, 2), {encoding: 'utf8'}); -    } -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-cache-map.js b/test/test-cache-map.js deleted file mode 100644 index 863c6ace..00000000 --- a/test/test-cache-map.js +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM({console}); -vm.execute([ -    'js/general/cache-map.js' -]); -const CacheMap = vm.get('CacheMap'); - - -function testConstructor() { -    const data = [ -        [false, () => new CacheMap(0)], -        [false, () => new CacheMap(1)], -        [false, () => new CacheMap(Number.MAX_VALUE)], -        [true,  () => new CacheMap(-1)], -        [true,  () => new CacheMap(1.5)], -        [true,  () => new CacheMap(Number.NaN)], -        [true,  () => new CacheMap(Number.POSITIVE_INFINITY)], -        [true,  () => new CacheMap('a')] -    ]; - -    for (const [throws, create] of data) { -        if (throws) { -            assert.throws(create); -        } else { -            assert.doesNotThrow(create); -        } -    } -} - -function testApi() { -    const data = [ -        { -            maxSize: 1, -            expectedSize: 0, -            calls: [] -        }, -        { -            maxSize: 10, -            expectedSize: 1, -            calls: [ -                {func: 'get', args: ['a1-b-c'],     returnValue: void 0}, -                {func: 'has', args: ['a1-b-c'],     returnValue: false}, -                {func: 'set', args: ['a1-b-c', 32], returnValue: void 0}, -                {func: 'get', args: ['a1-b-c'],     returnValue: 32}, -                {func: 'has', args: ['a1-b-c'],     returnValue: true} -            ] -        }, -        { -            maxSize: 10, -            expectedSize: 2, -            calls: [ -                {func: 'set', args: ['a1-b-c', 32], returnValue: void 0}, -                {func: 'get', args: ['a1-b-c'],     returnValue: 32}, -                {func: 'set', args: ['a1-b-c', 64], returnValue: void 0}, -                {func: 'get', args: ['a1-b-c'],     returnValue: 64}, -                {func: 'set', args: ['a2-b-c', 96], returnValue: void 0}, -                {func: 'get', args: ['a2-b-c'],     returnValue: 96} -            ] -        }, -        { -            maxSize: 2, -            expectedSize: 2, -            calls: [ -                {func: 'has', args: ['a1-b-c'],    returnValue: false}, -                {func: 'has', args: ['a2-b-c'],    returnValue: false}, -                {func: 'has', args: ['a3-b-c'],    returnValue: false}, -                {func: 'set', args: ['a1-b-c', 1], returnValue: void 0}, -                {func: 'has', args: ['a1-b-c'],    returnValue: true}, -                {func: 'has', args: ['a2-b-c'],    returnValue: false}, -                {func: 'has', args: ['a3-b-c'],    returnValue: false}, -                {func: 'set', args: ['a2-b-c', 2], returnValue: void 0}, -                {func: 'has', args: ['a1-b-c'],    returnValue: true}, -                {func: 'has', args: ['a2-b-c'],    returnValue: true}, -                {func: 'has', args: ['a3-b-c'],    returnValue: false}, -                {func: 'set', args: ['a3-b-c', 3], returnValue: void 0}, -                {func: 'has', args: ['a1-b-c'],    returnValue: false}, -                {func: 'has', args: ['a2-b-c'],    returnValue: true}, -                {func: 'has', args: ['a3-b-c'],    returnValue: true} -            ] -        } -    ]; - -    for (const {maxSize, expectedSize, calls} of data) { -        const cache = new CacheMap(maxSize); -        assert.strictEqual(cache.maxSize, maxSize); -        for (const call of calls) { -            const {func, args} = call; -            let returnValue; -            switch (func) { -                case 'get': returnValue = cache.get(...args); break; -                case 'set': returnValue = cache.set(...args); break; -                case 'has': returnValue = cache.has(...args); break; -                case 'clear': returnValue = cache.clear(...args); break; -            } -            if (Object.prototype.hasOwnProperty.call(call, 'returnValue')) { -                const {returnValue: expectedReturnValue} = call; -                assert.deepStrictEqual(returnValue, expectedReturnValue); -            } -        } -        assert.strictEqual(cache.size, expectedSize); -    } -} - - -function main() { -    testConstructor(); -    testApi(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-core.js b/test/test-core.js deleted file mode 100644 index b42f8cf2..00000000 --- a/test/test-core.js +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM(); -vm.execute([ -    'js/core.js' -]); -const [DynamicProperty, deepEqual] = vm.get(['DynamicProperty', 'deepEqual']); - - -function testDynamicProperty() { -    const data = [ -        { -            initialValue: 0, -            operations: [ -                { -                    operation: null, -                    expectedDefaultValue: 0, -                    expectedValue: 0, -                    expectedOverrideCount: 0, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'set.defaultValue', -                    args: [1], -                    expectedDefaultValue: 1, -                    expectedValue: 1, -                    expectedOverrideCount: 0, -                    expeectedEventOccurred: true -                }, -                { -                    operation: 'set.defaultValue', -                    args: [1], -                    expectedDefaultValue: 1, -                    expectedValue: 1, -                    expectedOverrideCount: 0, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'set.defaultValue', -                    args: [0], -                    expectedDefaultValue: 0, -                    expectedValue: 0, -                    expectedOverrideCount: 0, -                    expeectedEventOccurred: true -                }, -                { -                    operation: 'setOverride', -                    args: [8], -                    expectedDefaultValue: 0, -                    expectedValue: 8, -                    expectedOverrideCount: 1, -                    expeectedEventOccurred: true -                }, -                { -                    operation: 'setOverride', -                    args: [16], -                    expectedDefaultValue: 0, -                    expectedValue: 8, -                    expectedOverrideCount: 2, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'setOverride', -                    args: [32, 1], -                    expectedDefaultValue: 0, -                    expectedValue: 32, -                    expectedOverrideCount: 3, -                    expeectedEventOccurred: true -                }, -                { -                    operation: 'setOverride', -                    args: [64, -1], -                    expectedDefaultValue: 0, -                    expectedValue: 32, -                    expectedOverrideCount: 4, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'clearOverride', -                    args: [-4], -                    expectedDefaultValue: 0, -                    expectedValue: 32, -                    expectedOverrideCount: 3, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'clearOverride', -                    args: [-3], -                    expectedDefaultValue: 0, -                    expectedValue: 32, -                    expectedOverrideCount: 2, -                    expeectedEventOccurred: false -                }, -                { -                    operation: 'clearOverride', -                    args: [-2], -                    expectedDefaultValue: 0, -                    expectedValue: 64, -                    expectedOverrideCount: 1, -                    expeectedEventOccurred: true -                }, -                { -                    operation: 'clearOverride', -                    args: [-1], -                    expectedDefaultValue: 0, -                    expectedValue: 0, -                    expectedOverrideCount: 0, -                    expeectedEventOccurred: true -                } -            ] -        } -    ]; - -    for (const {initialValue, operations} of data) { -        const property = new DynamicProperty(initialValue); -        const overrideTokens = []; -        let eventOccurred = false; -        const onChange = () => { eventOccurred = true; }; -        property.on('change', onChange); -        for (const {operation, args, expectedDefaultValue, expectedValue, expectedOverrideCount, expeectedEventOccurred} of operations) { -            eventOccurred = false; -            switch (operation) { -                case 'set.defaultValue': property.defaultValue = args[0]; break; -                case 'setOverride': overrideTokens.push(property.setOverride(...args)); break; -                case 'clearOverride': property.clearOverride(overrideTokens[overrideTokens.length + args[0]]); break; -            } -            assert.strictEqual(eventOccurred, expeectedEventOccurred); -            assert.strictEqual(property.defaultValue, expectedDefaultValue); -            assert.strictEqual(property.value, expectedValue); -            assert.strictEqual(property.overrideCount, expectedOverrideCount); -        } -        property.off('change', onChange); -    } -} - -function testDeepEqual() { -    const data = [ -        // Simple tests -        { -            value1: 0, -            value2: 0, -            expected: true -        }, -        { -            value1: null, -            value2: null, -            expected: true -        }, -        { -            value1: 'test', -            value2: 'test', -            expected: true -        }, -        { -            value1: true, -            value2: true, -            expected: true -        }, -        { -            value1: 0, -            value2: 1, -            expected: false -        }, -        { -            value1: null, -            value2: false, -            expected: false -        }, -        { -            value1: 'test1', -            value2: 'test2', -            expected: false -        }, -        { -            value1: true, -            value2: false, -            expected: false -        }, - -        // Simple object tests -        { -            value1: {}, -            value2: {}, -            expected: true -        }, -        { -            value1: {}, -            value2: [], -            expected: false -        }, -        { -            value1: [], -            value2: [], -            expected: true -        }, -        { -            value1: {}, -            value2: null, -            expected: false -        }, - -        // Complex object tests -        { -            value1: [1], -            value2: [], -            expected: false -        }, -        { -            value1: [1], -            value2: [1], -            expected: true -        }, -        { -            value1: [1], -            value2: [2], -            expected: false -        }, - -        { -            value1: {}, -            value2: {test: 1}, -            expected: false -        }, -        { -            value1: {test: 1}, -            value2: {test: 1}, -            expected: true -        }, -        { -            value1: {test: 1}, -            value2: {test: {test2: false}}, -            expected: false -        }, -        { -            value1: {test: {test2: true}}, -            value2: {test: {test2: false}}, -            expected: false -        }, -        { -            value1: {test: {test2: [true]}}, -            value2: {test: {test2: [true]}}, -            expected: true -        }, - -        // Recursive -        { -            value1: (() => { const x = {}; x.x = x; return x; })(), -            value2: (() => { const x = {}; x.x = x; return x; })(), -            expected: false -        } -    ]; - -    let index = 0; -    for (const {value1, value2, expected} of data) { -        const actual1 = deepEqual(value1, value2); -        assert.strictEqual(actual1, expected, `Failed for test ${index}`); - -        const actual2 = deepEqual(value2, value1); -        assert.strictEqual(actual2, expected, `Failed for test ${index}`); - -        ++index; -    } -} - - -function main() { -    testDynamicProperty(); -    testDeepEqual(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-database.js b/test/test-database.js deleted file mode 100644 index c4cd504a..00000000 --- a/test/test-database.js +++ /dev/null @@ -1,887 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const path = require('path'); -const assert = require('assert'); -const {createDictionaryArchive, testMain} = require('../dev/util'); -const {DatabaseVM, DatabaseVMDictionaryImporterMediaLoader} = require('../dev/database-vm'); - - -const vm = new DatabaseVM(); -vm.execute([ -    'js/core.js', -    'js/general/cache-map.js', -    'js/data/json-schema.js', -    'js/media/media-util.js', -    'js/language/dictionary-importer.js', -    'js/data/database.js', -    'js/language/dictionary-database.js' -]); -const DictionaryImporter = vm.get('DictionaryImporter'); -const DictionaryDatabase = vm.get('DictionaryDatabase'); - - -function createTestDictionaryArchive(dictionary, dictionaryName) { -    const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', dictionary); -    return createDictionaryArchive(dictionaryDirectory, dictionaryName); -} - - -function createDictionaryImporter(onProgress) { -    const dictionaryImporterMediaLoader = new DatabaseVMDictionaryImporterMediaLoader(); -    return new DictionaryImporter(dictionaryImporterMediaLoader, (...args) => { -        const {stepIndex, stepCount, index, count} = args[0]; -        assert.ok(stepIndex < stepCount); -        assert.ok(index <= count); -        if (typeof onProgress === 'function') { -            onProgress(...args); -        } -    }); -} - - -function countDictionaryDatabaseEntriesWithTerm(dictionaryDatabaseEntries, term) { -    return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.term === term ? 1 : 0)), 0); -} - -function countDictionaryDatabaseEntriesWithReading(dictionaryDatabaseEntries, reading) { -    return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.reading === reading ? 1 : 0)), 0); -} - -function countMetasWithMode(metas, mode) { -    return metas.reduce((i, v) => (i + (v.mode === mode ? 1 : 0)), 0); -} - -function countKanjiWithCharacter(kanji, character) { -    return kanji.reduce((i, v) => (i + (v.character === character ? 1 : 0)), 0); -} - - -function clearDatabase(timeout) { -    return new Promise((resolve, reject) => { -        let timer = setTimeout(() => { -            timer = null; -            reject(new Error(`clearDatabase failed to resolve after ${timeout}ms`)); -        }, timeout); - -        (async () => { -            const indexedDB = vm.indexedDB; -            for (const {name} of await indexedDB.databases()) { -                await new Promise((resolve2, reject2) => { -                    const request = indexedDB.deleteDatabase(name); -                    request.onerror = (e) => reject2(e); -                    request.onsuccess = () => resolve2(); -                }); -            } -            if (timer !== null) { -                clearTimeout(timer); -            } -            resolve(); -        })(); -    }); -} - - -async function testDatabase1() { -    // Load dictionary data -    const testDictionary = createTestDictionaryArchive('valid-dictionary1'); -    const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); -    const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string')); - -    const title = testDictionaryIndex.title; -    const titles = new Map([ -        [title, {priority: 0, allowSecondarySearches: false}] -    ]); - -    // Setup iteration data -    const iterations = [ -        { -            cleanup: async () => { -                // Test purge -                await dictionaryDatabase.purge(); -                await testDatabaseEmpty1(dictionaryDatabase); -            } -        }, -        { -            cleanup: async () => { -                // Test deleteDictionary -                let progressEvent = false; -                await dictionaryDatabase.deleteDictionary( -                    title, -                    1000, -                    () => { -                        progressEvent = true; -                    } -                ); -                assert.ok(progressEvent); - -                await testDatabaseEmpty1(dictionaryDatabase); -            } -        }, -        { -            cleanup: async () => {} -        } -    ]; - -    // Setup database -    const dictionaryDatabase = new DictionaryDatabase(); -    await dictionaryDatabase.prepare(); - -    for (const {cleanup} of iterations) { -        const expectedSummary = { -            title, -            revision: 'test', -            sequenced: true, -            version: 3, -            importDate: 0, -            prefixWildcardsSupported: true, -            counts: { -                kanji: {total: 2}, -                kanjiMeta: {total: 6, freq: 6}, -                media: {total: 4}, -                tagMeta: {total: 15}, -                termMeta: {total: 38, freq: 31, pitch: 7}, -                terms: {total: 21} -            } -        }; - -        // Import data -        let progressEvent = false; -        const dictionaryImporter = createDictionaryImporter(() => { progressEvent = true; }); -        const {result, errors} = await dictionaryImporter.importDictionary( -            dictionaryDatabase, -            testDictionarySource, -            {prefixWildcardsSupported: true} -        ); -        expectedSummary.importDate = result.importDate; -        vm.assert.deepStrictEqual(errors, []); -        vm.assert.deepStrictEqual(result, expectedSummary); -        assert.ok(progressEvent); - -        // Get info summary -        const info = await dictionaryDatabase.getDictionaryInfo(); -        vm.assert.deepStrictEqual(info, [expectedSummary]); - -        // Get counts -        const counts = await dictionaryDatabase.getDictionaryCounts( -            info.map((v) => v.title), -            true -        ); -        vm.assert.deepStrictEqual(counts, { -            counts: [{kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4}], -            total: {kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4} -        }); - -        // Test find* functions -        await testFindTermsBulkTest1(dictionaryDatabase, titles); -        await testTindTermsExactBulk1(dictionaryDatabase, titles); -        await testFindTermsBySequenceBulk1(dictionaryDatabase, title); -        await testFindTermMetaBulk1(dictionaryDatabase, titles); -        await testFindKanjiBulk1(dictionaryDatabase, titles); -        await testFindKanjiMetaBulk1(dictionaryDatabase, titles); -        await testFindTagForTitle1(dictionaryDatabase, title); - -        // Cleanup -        await cleanup(); -    } - -    await dictionaryDatabase.close(); -} - -async function testDatabaseEmpty1(database) { -    const info = await database.getDictionaryInfo(); -    vm.assert.deepStrictEqual(info, []); - -    const counts = await database.getDictionaryCounts([], true); -    vm.assert.deepStrictEqual(counts, { -        counts: [], -        total: {kanji: 0, kanjiMeta: 0, terms: 0, termMeta: 0, tagMeta: 0, media: 0} -    }); -} - -async function testFindTermsBulkTest1(database, titles) { -    const data = [ -        { -            inputs: [ -                { -                    matchType: null, -                    termList: ['打', '打つ', '打ち込む'] -                }, -                { -                    matchType: null, -                    termList: ['だ', 'ダース', 'うつ', 'ぶつ', 'うちこむ', 'ぶちこむ'] -                }, -                { -                    matchType: 'prefix', -                    termList: ['打'] -                } -            ], -            expectedResults: { -                total: 10, -                terms: [ -                    ['打', 2], -                    ['打つ', 4], -                    ['打ち込む', 4] -                ], -                readings: [ -                    ['だ', 1], -                    ['ダース', 1], -                    ['うつ', 2], -                    ['ぶつ', 2], -                    ['うちこむ', 2], -                    ['ぶちこむ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    matchType: null, -                    termList: ['込む'] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        }, -        { -            inputs: [ -                { -                    matchType: 'suffix', -                    termList: ['込む'] -                } -            ], -            expectedResults: { -                total: 4, -                terms: [ -                    ['打ち込む', 4] -                ], -                readings: [ -                    ['うちこむ', 2], -                    ['ぶちこむ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    matchType: null, -                    termList: [] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {termList, matchType} of inputs) { -            const results = await database.findTermsBulk(termList, titles, matchType); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [term, count] of expectedResults.terms) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count); -            } -            for (const [reading, count] of expectedResults.readings) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count); -            } -        } -    } -} - -async function testTindTermsExactBulk1(database, titles) { -    const data = [ -        { -            inputs: [ -                { -                    termList: [ -                        {term: '打', reading: 'だ'}, -                        {term: '打つ', reading: 'うつ'}, -                        {term: '打ち込む', reading: 'うちこむ'} -                    ] -                } -            ], -            expectedResults: { -                total: 5, -                terms: [ -                    ['打', 1], -                    ['打つ', 2], -                    ['打ち込む', 2] -                ], -                readings: [ -                    ['だ', 1], -                    ['うつ', 2], -                    ['うちこむ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    termList: [ -                        {term: '打', reading: 'だ?'}, -                        {term: '打つ', reading: 'うつ?'}, -                        {term: '打ち込む', reading: 'うちこむ?'} -                    ] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        }, -        { -            inputs: [ -                { -                    termList: [ -                        {term: '打つ', reading: 'うつ'}, -                        {term: '打つ', reading: 'ぶつ'} -                    ] -                } -            ], -            expectedResults: { -                total: 4, -                terms: [ -                    ['打つ', 4] -                ], -                readings: [ -                    ['うつ', 2], -                    ['ぶつ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    termList: [ -                        {term: '打つ', reading: 'うちこむ'} -                    ] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        }, -        { -            inputs: [ -                { -                    termList: [] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {termList} of inputs) { -            const results = await database.findTermsExactBulk(termList, titles); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [term, count] of expectedResults.terms) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count); -            } -            for (const [reading, count] of expectedResults.readings) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count); -            } -        } -    } -} - -async function testFindTermsBySequenceBulk1(database, mainDictionary) { -    const data = [ -        { -            inputs: [ -                { -                    sequenceList: [1, 2, 3, 4, 5] -                } -            ], -            expectedResults: { -                total: 11, -                terms: [ -                    ['打', 2], -                    ['打つ', 4], -                    ['打ち込む', 4], -                    ['画像', 1] -                ], -                readings: [ -                    ['だ', 1], -                    ['ダース', 1], -                    ['うつ', 2], -                    ['ぶつ', 2], -                    ['うちこむ', 2], -                    ['ぶちこむ', 2], -                    ['がぞう', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [1] -                } -            ], -            expectedResults: { -                total: 1, -                terms: [ -                    ['打', 1] -                ], -                readings: [ -                    ['だ', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [2] -                } -            ], -            expectedResults: { -                total: 1, -                terms: [ -                    ['打', 1] -                ], -                readings: [ -                    ['ダース', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [3] -                } -            ], -            expectedResults: { -                total: 4, -                terms: [ -                    ['打つ', 4] -                ], -                readings: [ -                    ['うつ', 2], -                    ['ぶつ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [4] -                } -            ], -            expectedResults: { -                total: 4, -                terms: [ -                    ['打ち込む', 4] -                ], -                readings: [ -                    ['うちこむ', 2], -                    ['ぶちこむ', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [5] -                } -            ], -            expectedResults: { -                total: 1, -                terms: [ -                    ['画像', 1] -                ], -                readings: [ -                    ['がぞう', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [-1] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        }, -        { -            inputs: [ -                { -                    sequenceList: [] -                } -            ], -            expectedResults: { -                total: 0, -                terms: [], -                readings: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {sequenceList} of inputs) { -            const results = await database.findTermsBySequenceBulk(sequenceList.map((query) => ({query, dictionary: mainDictionary}))); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [term, count] of expectedResults.terms) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count); -            } -            for (const [reading, count] of expectedResults.readings) { -                assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count); -            } -        } -    } -} - -async function testFindTermMetaBulk1(database, titles) { -    const data = [ -        { -            inputs: [ -                { -                    termList: ['打'] -                } -            ], -            expectedResults: { -                total: 11, -                modes: [ -                    ['freq', 11] -                ] -            } -        }, -        { -            inputs: [ -                { -                    termList: ['打つ'] -                } -            ], -            expectedResults: { -                total: 10, -                modes: [ -                    ['freq', 10] -                ] -            } -        }, -        { -            inputs: [ -                { -                    termList: ['打ち込む'] -                } -            ], -            expectedResults: { -                total: 12, -                modes: [ -                    ['freq', 10], -                    ['pitch', 2] -                ] -            } -        }, -        { -            inputs: [ -                { -                    termList: ['?'] -                } -            ], -            expectedResults: { -                total: 0, -                modes: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {termList} of inputs) { -            const results = await database.findTermMetaBulk(termList, titles); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [mode, count] of expectedResults.modes) { -                assert.strictEqual(countMetasWithMode(results, mode), count); -            } -        } -    } -} - -async function testFindKanjiBulk1(database, titles) { -    const data = [ -        { -            inputs: [ -                { -                    kanjiList: ['打'] -                } -            ], -            expectedResults: { -                total: 1, -                kanji: [ -                    ['打', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    kanjiList: ['込'] -                } -            ], -            expectedResults: { -                total: 1, -                kanji: [ -                    ['込', 1] -                ] -            } -        }, -        { -            inputs: [ -                { -                    kanjiList: ['?'] -                } -            ], -            expectedResults: { -                total: 0, -                kanji: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {kanjiList} of inputs) { -            const results = await database.findKanjiBulk(kanjiList, titles); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [kanji, count] of expectedResults.kanji) { -                assert.strictEqual(countKanjiWithCharacter(results, kanji), count); -            } -        } -    } -} - -async function testFindKanjiMetaBulk1(database, titles) { -    const data = [ -        { -            inputs: [ -                { -                    kanjiList: ['打'] -                } -            ], -            expectedResults: { -                total: 3, -                modes: [ -                    ['freq', 3] -                ] -            } -        }, -        { -            inputs: [ -                { -                    kanjiList: ['込'] -                } -            ], -            expectedResults: { -                total: 3, -                modes: [ -                    ['freq', 3] -                ] -            } -        }, -        { -            inputs: [ -                { -                    kanjiList: ['?'] -                } -            ], -            expectedResults: { -                total: 0, -                modes: [] -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {kanjiList} of inputs) { -            const results = await database.findKanjiMetaBulk(kanjiList, titles); -            assert.strictEqual(results.length, expectedResults.total); -            for (const [mode, count] of expectedResults.modes) { -                assert.strictEqual(countMetasWithMode(results, mode), count); -            } -        } -    } -} - -async function testFindTagForTitle1(database, title) { -    const data = [ -        { -            inputs: [ -                { -                    name: 'E1' -                } -            ], -            expectedResults: { -                value: {category: 'default', dictionary: title, name: 'E1', notes: 'example tag 1', order: 0, score: 0} -            } -        }, -        { -            inputs: [ -                { -                    name: 'K1' -                } -            ], -            expectedResults: { -                value: {category: 'default', dictionary: title, name: 'K1', notes: 'example kanji tag 1', order: 0, score: 0} -            } -        }, -        { -            inputs: [ -                { -                    name: 'kstat1' -                } -            ], -            expectedResults: { -                value: {category: 'class', dictionary: title, name: 'kstat1', notes: 'kanji stat 1', order: 0, score: 0} -            } -        }, -        { -            inputs: [ -                { -                    name: 'invalid' -                } -            ], -            expectedResults: { -                value: null -            } -        } -    ]; - -    for (const {inputs, expectedResults} of data) { -        for (const {name} of inputs) { -            const result = await database.findTagForTitle(name, title); -            vm.assert.deepStrictEqual(result, expectedResults.value); -        } -    } -} - - -async function testDatabase2() { -    // Load dictionary data -    const testDictionary = createTestDictionaryArchive('valid-dictionary1'); -    const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); -    const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string')); - -    const title = testDictionaryIndex.title; -    const titles = new Map([ -        [title, {priority: 0, allowSecondarySearches: false}] -    ]); - -    // Setup database -    const dictionaryDatabase = new DictionaryDatabase(); - -    // Error: not prepared -    await assert.rejects(async () => await dictionaryDatabase.deleteDictionary(title, 1000)); -    await assert.rejects(async () => await dictionaryDatabase.findTermsBulk(['?'], titles, null)); -    await assert.rejects(async () => await dictionaryDatabase.findTermsExactBulk([{term: '?', reading: '?'}], titles)); -    await assert.rejects(async () => await dictionaryDatabase.findTermsBySequenceBulk([{query: 1, dictionary: title}])); -    await assert.rejects(async () => await dictionaryDatabase.findTermMetaBulk(['?'], titles)); -    await assert.rejects(async () => await dictionaryDatabase.findTermMetaBulk(['?'], titles)); -    await assert.rejects(async () => await dictionaryDatabase.findKanjiBulk(['?'], titles)); -    await assert.rejects(async () => await dictionaryDatabase.findKanjiMetaBulk(['?'], titles)); -    await assert.rejects(async () => await dictionaryDatabase.findTagForTitle('tag', title)); -    await assert.rejects(async () => await dictionaryDatabase.getDictionaryInfo()); -    await assert.rejects(async () => await dictionaryDatabase.getDictionaryCounts(titles, true)); -    await assert.rejects(async () => await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {})); - -    await dictionaryDatabase.prepare(); - -    // Error: already prepared -    await assert.rejects(async () => await dictionaryDatabase.prepare()); - -    await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {}); - -    // Error: dictionary already imported -    await assert.rejects(async () => await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {})); - -    await dictionaryDatabase.close(); -} - - -async function testDatabase3() { -    const invalidDictionaries = [ -        'invalid-dictionary1', -        'invalid-dictionary2', -        'invalid-dictionary3', -        'invalid-dictionary4', -        'invalid-dictionary5', -        'invalid-dictionary6' -    ]; - -    // Setup database -    const dictionaryDatabase = new DictionaryDatabase(); -    await dictionaryDatabase.prepare(); - -    for (const invalidDictionary of invalidDictionaries) { -        const testDictionary = createTestDictionaryArchive(invalidDictionary); -        const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'}); - -        let error = null; -        try { -            await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, {}); -        } catch (e) { -            error = e; -        } - -        if (error === null) { -            assert.ok(false, `Expected an error while importing ${invalidDictionary}`); -        } else { -            const prefix = 'Dictionary has invalid data'; -            const message = error.message; -            assert.ok(typeof message, 'string'); -            assert.ok(message.startsWith(prefix), `Expected error message to start with '${prefix}': ${message}`); -        } -    } - -    await dictionaryDatabase.close(); -} - - -async function main() { -    const clearTimeout = 5000; -    try { -        await testDatabase1(); -        await clearDatabase(clearTimeout); - -        await testDatabase2(); -        await clearDatabase(clearTimeout); - -        await testDatabase3(); -        await clearDatabase(clearTimeout); -    } catch (e) { -        console.log(e); -        process.exit(-1); -        throw e; -    } -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-document-util.js b/test/test-document-util.js deleted file mode 100644 index a2458b61..00000000 --- a/test/test-document-util.js +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {JSDOM} = require('jsdom'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - - -// DOMRect class definition -class DOMRect { -    constructor(x, y, width, height) { -        this._x = x; -        this._y = y; -        this._width = width; -        this._height = height; -    } - -    get x() { return this._x; } -    get y() { return this._y; } -    get width() { return this._width; } -    get height() { return this._height; } -    get left() { return this._x + Math.min(0, this._width); } -    get right() { return this._x + Math.max(0, this._width); } -    get top() { return this._y + Math.min(0, this._height); } -    get bottom() { return this._y + Math.max(0, this._height); } -} - - -function createJSDOM(fileName) { -    const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); -    const dom = new JSDOM(domSource); -    const document = dom.window.document; -    const window = dom.window; - -    // Define innerText setter as an alias for textContent setter -    Object.defineProperty(window.HTMLDivElement.prototype, 'innerText', { -        set(value) { this.textContent = value; } -    }); - -    // Placeholder for feature detection -    document.caretRangeFromPoint = () => null; - -    return dom; -} - -function querySelectorChildOrSelf(element, selector) { -    return selector ? element.querySelector(selector) : element; -} - -function getChildTextNodeOrSelf(dom, node) { -    if (node === null) { return null; } -    const Node = dom.window.Node; -    const childNode = node.firstChild; -    return (childNode !== null && childNode.nodeType === Node.TEXT_NODE ? childNode : node); -} - -function getPrototypeOfOrNull(value) { -    try { -        return Object.getPrototypeOf(value); -    } catch (e) { -        return null; -    } -} - -function findImposterElement(document) { -    // Finds the imposter element based on it's z-index style -    return document.querySelector('div[style*="2147483646"]>*'); -} - - -async function testDocument1() { -    const dom = createJSDOM(path.join(__dirname, 'data', 'html', 'test-document1.html')); -    const window = dom.window; -    const document = window.document; -    const Node = window.Node; -    const Range = window.Range; - -    const vm = new VM({document, window, Range, Node}); -    vm.execute([ -        'js/data/sandbox/string-util.js', -        'js/dom/dom-text-scanner.js', -        'js/dom/text-source-range.js', -        'js/dom/text-source-element.js', -        'js/dom/document-util.js' -    ]); -    const [DOMTextScanner, TextSourceRange, TextSourceElement, DocumentUtil] = vm.get([ -        'DOMTextScanner', -        'TextSourceRange', -        'TextSourceElement', -        'DocumentUtil' -    ]); - -    try { -        await testDocumentTextScanningFunctions(dom, {DocumentUtil, TextSourceRange, TextSourceElement}); -        await testTextSourceRangeSeekFunctions(dom, {DOMTextScanner}); -    } finally { -        window.close(); -    } -} - -async function testDocumentTextScanningFunctions(dom, {DocumentUtil, TextSourceRange, TextSourceElement}) { -    const document = dom.window.document; - -    for (const testElement of document.querySelectorAll('.test[data-test-type=scan]')) { -        // Get test parameters -        let { -            elementFromPointSelector, -            caretRangeFromPointSelector, -            startNodeSelector, -            startOffset, -            endNodeSelector, -            endOffset, -            resultType, -            sentenceScanExtent, -            sentence, -            hasImposter, -            terminateAtNewlines -        } = testElement.dataset; - -        const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector); -        const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector); -        const startNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, startNodeSelector)); -        const endNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, endNodeSelector)); - -        startOffset = parseInt(startOffset, 10); -        endOffset = parseInt(endOffset, 10); -        sentenceScanExtent = parseInt(sentenceScanExtent, 10); -        terminateAtNewlines = (terminateAtNewlines !== 'false'); - -        assert.notStrictEqual(elementFromPointValue, null); -        assert.notStrictEqual(caretRangeFromPointValue, null); -        assert.notStrictEqual(startNode, null); -        assert.notStrictEqual(endNode, null); - -        // Setup functions -        document.elementFromPoint = () => elementFromPointValue; - -        document.caretRangeFromPoint = (x, y) => { -            const imposter = getChildTextNodeOrSelf(dom, findImposterElement(document)); -            assert.strictEqual(!!imposter, hasImposter === 'true'); - -            const range = document.createRange(); -            range.setStart(imposter ? imposter : startNode, startOffset); -            range.setEnd(imposter ? imposter : startNode, endOffset); - -            // Override getClientRects to return a rect guaranteed to contain (x, y) -            range.getClientRects = () => [new DOMRect(x - 1, y - 1, 2, 2)]; -            return range; -        }; - -        // Test docRangeFromPoint -        const source = DocumentUtil.getRangeFromPoint(0, 0, { -            deepContentScan: false, -            normalizeCssZoom: true -        }); -        switch (resultType) { -            case 'TextSourceRange': -                assert.strictEqual(getPrototypeOfOrNull(source), TextSourceRange.prototype); -                break; -            case 'TextSourceElement': -                assert.strictEqual(getPrototypeOfOrNull(source), TextSourceElement.prototype); -                break; -            case 'null': -                assert.strictEqual(source, null); -                break; -            default: -                assert.ok(false); -                break; -        } -        if (source === null) { continue; } - -        // Sentence info -        const terminatorString = '…。..??!!'; -        const terminatorMap = new Map(); -        for (const char of terminatorString) { -            terminatorMap.set(char, [false, true]); -        } -        const quoteArray = [['「', '」'], ['『', '』'], ['\'', '\''], ['"', '"']]; -        const forwardQuoteMap = new Map(); -        const backwardQuoteMap = new Map(); -        for (const [char1, char2] of quoteArray) { -            forwardQuoteMap.set(char1, [char2, false]); -            backwardQuoteMap.set(char2, [char1, false]); -        } - -        // Test docSentenceExtract -        const sentenceActual = DocumentUtil.extractSentence( -            source, -            false, -            sentenceScanExtent, -            terminateAtNewlines, -            terminatorMap, -            forwardQuoteMap, -            backwardQuoteMap -        ).text; -        assert.strictEqual(sentenceActual, sentence); - -        // Clean -        source.cleanup(); -    } -} - -async function testTextSourceRangeSeekFunctions(dom, {DOMTextScanner}) { -    const document = dom.window.document; - -    for (const testElement of document.querySelectorAll('.test[data-test-type=text-source-range-seek]')) { -        // Get test parameters -        let { -            seekNodeSelector, -            seekNodeIsText, -            seekOffset, -            seekLength, -            seekDirection, -            expectedResultNodeSelector, -            expectedResultNodeIsText, -            expectedResultOffset, -            expectedResultContent -        } = testElement.dataset; - -        seekOffset = parseInt(seekOffset, 10); -        seekLength = parseInt(seekLength, 10); -        expectedResultOffset = parseInt(expectedResultOffset, 10); - -        let seekNode = testElement.querySelector(seekNodeSelector); -        if (seekNodeIsText === 'true') { -            seekNode = seekNode.firstChild; -        } - -        let expectedResultNode = testElement.querySelector(expectedResultNodeSelector); -        if (expectedResultNodeIsText === 'true') { -            expectedResultNode = expectedResultNode.firstChild; -        } - -        const {node, offset, content} = ( -            seekDirection === 'forward' ? -            new DOMTextScanner(seekNode, seekOffset, true, false).seek(seekLength) : -            new DOMTextScanner(seekNode, seekOffset, true, false).seek(-seekLength) -        ); - -        assert.strictEqual(node, expectedResultNode); -        assert.strictEqual(offset, expectedResultOffset); -        assert.strictEqual(content, expectedResultContent); -    } -} - - -async function main() { -    await testDocument1(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-hotkey-util.js b/test/test-hotkey-util.js deleted file mode 100644 index 34f1eb26..00000000 --- a/test/test-hotkey-util.js +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} - -function createHotkeyUtil() { -    const vm = new VM(); -    vm.execute(['js/input/hotkey-util.js']); -    const [HotkeyUtil] = vm.get(['HotkeyUtil']); -    return new HotkeyUtil(); -} - - -function testCommandConversions() { -    const data = [ -        {os: 'win', command: 'Alt+F', expectedCommand: 'Alt+F', expectedInput: {key: 'KeyF', modifiers: ['alt']}}, -        {os: 'win', command: 'F1',    expectedCommand: 'F1',    expectedInput: {key: 'F1', modifiers: []}}, - -        {os: 'win', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, -        {os: 'win', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, -        {os: 'win', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, - -        {os: 'mac', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, -        {os: 'mac', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'MacCtrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, -        {os: 'mac', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}, - -        {os: 'linux', command: 'Ctrl+Alt+Shift+F1',    expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, -        {os: 'linux', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}}, -        {os: 'linux', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}} -    ]; - -    const hotkeyUtil = createHotkeyUtil(); -    for (const {command, os, expectedInput, expectedCommand} of data) { -        hotkeyUtil.os = os; -        const input = clone(hotkeyUtil.convertCommandToInput(command)); -        assert.deepStrictEqual(input, expectedInput); -        const command2 = hotkeyUtil.convertInputToCommand(input.key, input.modifiers); -        assert.deepStrictEqual(command2, expectedCommand); -    } -} - -function testDisplayNames() { -    const data = [ -        {os: 'win', key: null,   modifiers: [], expected: ''}, -        {os: 'win', key: 'KeyF', modifiers: [], expected: 'F'}, -        {os: 'win', key: 'F1',   modifiers: [], expected: 'F1'}, -        {os: 'win', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, -        {os: 'win', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, -        {os: 'win', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, -        {os: 'win', key: null,   modifiers: ['alt'], expected: 'Alt'}, -        {os: 'win', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, -        {os: 'win', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, -        {os: 'win', key: null,   modifiers: ['shift'], expected: 'Shift'}, -        {os: 'win', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, -        {os: 'win', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, -        {os: 'win', key: null,   modifiers: ['meta'], expected: 'Windows'}, -        {os: 'win', key: 'KeyF', modifiers: ['meta'], expected: 'Windows + F'}, -        {os: 'win', key: 'F1',   modifiers: ['meta'], expected: 'Windows + F1'}, -        {os: 'win', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, -        {os: 'win', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, -        {os: 'win', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, - -        {os: 'mac', key: null,   modifiers: [], expected: ''}, -        {os: 'mac', key: 'KeyF', modifiers: [], expected: 'F'}, -        {os: 'mac', key: 'F1',   modifiers: [], expected: 'F1'}, -        {os: 'mac', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, -        {os: 'mac', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, -        {os: 'mac', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, -        {os: 'mac', key: null,   modifiers: ['alt'], expected: 'Opt'}, -        {os: 'mac', key: 'KeyF', modifiers: ['alt'], expected: 'Opt + F'}, -        {os: 'mac', key: 'F1',   modifiers: ['alt'], expected: 'Opt + F1'}, -        {os: 'mac', key: null,   modifiers: ['shift'], expected: 'Shift'}, -        {os: 'mac', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, -        {os: 'mac', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, -        {os: 'mac', key: null,   modifiers: ['meta'], expected: 'Cmd'}, -        {os: 'mac', key: 'KeyF', modifiers: ['meta'], expected: 'Cmd + F'}, -        {os: 'mac', key: 'F1',   modifiers: ['meta'], expected: 'Cmd + F1'}, -        {os: 'mac', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, -        {os: 'mac', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, -        {os: 'mac', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, - -        {os: 'linux', key: null,   modifiers: [], expected: ''}, -        {os: 'linux', key: 'KeyF', modifiers: [], expected: 'F'}, -        {os: 'linux', key: 'F1',   modifiers: [], expected: 'F1'}, -        {os: 'linux', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, -        {os: 'linux', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, -        {os: 'linux', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, -        {os: 'linux', key: null,   modifiers: ['alt'], expected: 'Alt'}, -        {os: 'linux', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, -        {os: 'linux', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, -        {os: 'linux', key: null,   modifiers: ['shift'], expected: 'Shift'}, -        {os: 'linux', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, -        {os: 'linux', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, -        {os: 'linux', key: null,   modifiers: ['meta'], expected: 'Super'}, -        {os: 'linux', key: 'KeyF', modifiers: ['meta'], expected: 'Super + F'}, -        {os: 'linux', key: 'F1',   modifiers: ['meta'], expected: 'Super + F1'}, -        {os: 'linux', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, -        {os: 'linux', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, -        {os: 'linux', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}, - -        {os: 'unknown', key: null,   modifiers: [], expected: ''}, -        {os: 'unknown', key: 'KeyF', modifiers: [], expected: 'F'}, -        {os: 'unknown', key: 'F1',   modifiers: [], expected: 'F1'}, -        {os: 'unknown', key: null,   modifiers: ['ctrl'], expected: 'Ctrl'}, -        {os: 'unknown', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'}, -        {os: 'unknown', key: 'F1',   modifiers: ['ctrl'], expected: 'Ctrl + F1'}, -        {os: 'unknown', key: null,   modifiers: ['alt'], expected: 'Alt'}, -        {os: 'unknown', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'}, -        {os: 'unknown', key: 'F1',   modifiers: ['alt'], expected: 'Alt + F1'}, -        {os: 'unknown', key: null,   modifiers: ['shift'], expected: 'Shift'}, -        {os: 'unknown', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'}, -        {os: 'unknown', key: 'F1',   modifiers: ['shift'], expected: 'Shift + F1'}, -        {os: 'unknown', key: null,   modifiers: ['meta'], expected: 'Meta'}, -        {os: 'unknown', key: 'KeyF', modifiers: ['meta'], expected: 'Meta + F'}, -        {os: 'unknown', key: 'F1',   modifiers: ['meta'], expected: 'Meta + F1'}, -        {os: 'unknown', key: null,   modifiers: ['mouse1'], expected: 'Mouse 1'}, -        {os: 'unknown', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'}, -        {os: 'unknown', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'} -    ]; - -    const hotkeyUtil = createHotkeyUtil(); -    for (const {os, key, modifiers, expected} of data) { -        hotkeyUtil.os = os; -        const displayName = hotkeyUtil.getInputDisplayValue(key, modifiers); -        assert.deepStrictEqual(displayName, expected); -    } -} - -function testSortModifiers() { -    const data = [ -        {modifiers: [], expected: []}, -        {modifiers: ['shift', 'alt', 'ctrl', 'mouse4', 'meta', 'mouse1', 'mouse0'], expected: ['meta', 'ctrl', 'alt', 'shift', 'mouse0', 'mouse1', 'mouse4']} -    ]; - -    const hotkeyUtil = createHotkeyUtil(); -    for (const {modifiers, expected} of data) { -        const modifiers2 = hotkeyUtil.sortModifiers(modifiers); -        assert.strictEqual(modifiers2, modifiers); -        assert.deepStrictEqual(modifiers2, expected); -    } -} - - -function main() { -    testCommandConversions(); -    testDisplayNames(); -    testSortModifiers(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-japanese-util.js b/test/test-japanese-util.js deleted file mode 100644 index 4395a11e..00000000 --- a/test/test-japanese-util.js +++ /dev/null @@ -1,881 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM(); -vm.execute([ -    'lib/wanakana.min.js', -    'js/language/sandbox/japanese-util.js', -    'js/general/text-source-map.js' -]); -const [JapaneseUtil, TextSourceMap, wanakana] = vm.get(['JapaneseUtil', 'TextSourceMap', 'wanakana']); -const jp = new JapaneseUtil(wanakana); - - -function testIsCodePointKanji() { -    const data = [ -        ['力方', true], -        ['\u53f1\u{20b9f}', true], -        ['かたカタ々kata、。?,.?', false], -        ['逸逸', true] -    ]; - -    for (const [characters, expected] of data) { -        for (const character of characters) { -            const codePoint = character.codePointAt(0); -            const actual = jp.isCodePointKanji(codePoint); -            assert.strictEqual(actual, expected, `isCodePointKanji failed for ${character} (\\u{${codePoint.toString(16)}})`); -        } -    } -} - -function testIsCodePointKana() { -    const data = [ -        ['かたカタ', true], -        ['力方々kata、。?,.?', false], -        ['\u53f1\u{20b9f}', false] -    ]; - -    for (const [characters, expected] of data) { -        for (const character of characters) { -            const codePoint = character.codePointAt(0); -            const actual = jp.isCodePointKana(codePoint); -            assert.strictEqual(actual, expected, `isCodePointKana failed for ${character} (\\u{${codePoint.toString(16)}})`); -        } -    } -} - -function testIsCodePointJapanese() { -    const data = [ -        ['かたカタ力方々、。?', true], -        ['\u53f1\u{20b9f}', true], -        ['kata,.?', false], -        ['逸逸', true] -    ]; - -    for (const [characters, expected] of data) { -        for (const character of characters) { -            const codePoint = character.codePointAt(0); -            const actual = jp.isCodePointJapanese(codePoint); -            assert.strictEqual(actual, expected, `isCodePointJapanese failed for ${character} (\\u{${codePoint.toString(16)}})`); -        } -    } -} - -function testIsStringEntirelyKana() { -    const data = [ -        ['かたかな', true], -        ['カタカナ', true], -        ['ひらがな', true], -        ['ヒラガナ', true], -        ['カタカナひらがな', true], -        ['かたカタ力方々、。?', false], -        ['\u53f1\u{20b9f}', false], -        ['kata,.?', false], -        ['かたカタ力方々、。?invalid', false], -        ['\u53f1\u{20b9f}invalid', false], -        ['kata,.?かた', false] -    ]; - -    for (const [string, expected] of data) { -        assert.strictEqual(jp.isStringEntirelyKana(string), expected); -    } -} - -function testIsStringPartiallyJapanese() { -    const data = [ -        ['かたかな', true], -        ['カタカナ', true], -        ['ひらがな', true], -        ['ヒラガナ', true], -        ['カタカナひらがな', true], -        ['かたカタ力方々、。?', true], -        ['\u53f1\u{20b9f}', true], -        ['kata,.?', false], -        ['かたカタ力方々、。?invalid', true], -        ['\u53f1\u{20b9f}invalid', true], -        ['kata,.?かた', true], -        ['逸逸', true] -    ]; - -    for (const [string, expected] of data) { -        assert.strictEqual(jp.isStringPartiallyJapanese(string), expected); -    } -} - -function testConvertKatakanaToHiragana() { -    const data = [ -        ['かたかな', 'かたかな'], -        ['ひらがな', 'ひらがな'], -        ['カタカナ', 'かたかな'], -        ['ヒラガナ', 'ひらがな'], -        ['カタカナかたかな', 'かたかなかたかな'], -        ['ヒラガナひらがな', 'ひらがなひらがな'], -        ['chikaraちからチカラ力', 'chikaraちからちから力'], -        ['katakana', 'katakana'], -        ['hiragana', 'hiragana'], -        ['カーナー', 'かあなあ'], -        ['カーナー', 'かーなー', true] -    ]; - -    for (const [string, expected, keepProlongedSoundMarks=false] of data) { -        assert.strictEqual(jp.convertKatakanaToHiragana(string, keepProlongedSoundMarks), expected); -    } -} - -function testConvertHiraganaToKatakana() { -    const data = [ -        ['かたかな', 'カタカナ'], -        ['ひらがな', 'ヒラガナ'], -        ['カタカナ', 'カタカナ'], -        ['ヒラガナ', 'ヒラガナ'], -        ['カタカナかたかな', 'カタカナカタカナ'], -        ['ヒラガナひらがな', 'ヒラガナヒラガナ'], -        ['chikaraちからチカラ力', 'chikaraチカラチカラ力'], -        ['katakana', 'katakana'], -        ['hiragana', 'hiragana'] -    ]; - -    for (const [string, expected] of data) { -        assert.strictEqual(jp.convertHiraganaToKatakana(string), expected); -    } -} - -function testConvertToRomaji() { -    const data = [ -        ['かたかな', 'katakana'], -        ['ひらがな', 'hiragana'], -        ['カタカナ', 'katakana'], -        ['ヒラガナ', 'hiragana'], -        ['カタカナかたかな', 'katakanakatakana'], -        ['ヒラガナひらがな', 'hiraganahiragana'], -        ['chikaraちからチカラ力', 'chikarachikarachikara力'], -        ['katakana', 'katakana'], -        ['hiragana', 'hiragana'] -    ]; - -    for (const [string, expected] of data) { -        assert.strictEqual(jp.convertToRomaji(string), expected); -    } -} - -function testConvertNumericToFullWidth() { -    const data = [ -        ['0123456789', '0123456789'], -        ['abcdefghij', 'abcdefghij'], -        ['カタカナ', 'カタカナ'], -        ['ひらがな', 'ひらがな'] -    ]; - -    for (const [string, expected] of data) { -        assert.strictEqual(jp.convertNumericToFullWidth(string), expected); -    } -} - -function testConvertHalfWidthKanaToFullWidth() { -    const data = [ -        ['0123456789', '0123456789'], -        ['abcdefghij', 'abcdefghij'], -        ['カタカナ', 'カタカナ'], -        ['ひらがな', 'ひらがな'], -        ['カキ', 'カキ', [1, 1]], -        ['ガキ', 'ガキ', [2, 1]], -        ['ニホン', 'ニホン', [1, 1, 1]], -        ['ニッポン', 'ニッポン', [1, 1, 2, 1]] -    ]; - -    for (const [string, expected, expectedSourceMapping] of data) { -        const sourceMap = new TextSourceMap(string); -        const actual1 = jp.convertHalfWidthKanaToFullWidth(string, null); -        const actual2 = jp.convertHalfWidthKanaToFullWidth(string, sourceMap); -        assert.strictEqual(actual1, expected); -        assert.strictEqual(actual2, expected); -        if (typeof expectedSourceMapping !== 'undefined') { -            assert.ok(sourceMap.equals(new TextSourceMap(string, expectedSourceMapping))); -        } -    } -} - -function testConvertAlphabeticToKana() { -    const data = [ -        ['0123456789', '0123456789'], -        ['abcdefghij', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]], -        ['ABCDEFGHIJ', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]], // wanakana.toHiragana converts text to lower case -        ['カタカナ', 'カタカナ'], -        ['ひらがな', 'ひらがな'], -        ['chikara', 'ちから', [3, 2, 2]], -        ['CHIKARA', 'ちから', [3, 2, 2]] -    ]; - -    for (const [string, expected, expectedSourceMapping] of data) { -        const sourceMap = new TextSourceMap(string); -        const actual1 = jp.convertAlphabeticToKana(string, null); -        const actual2 = jp.convertAlphabeticToKana(string, sourceMap); -        assert.strictEqual(actual1, expected); -        assert.strictEqual(actual2, expected); -        if (typeof expectedSourceMapping !== 'undefined') { -            assert.ok(sourceMap.equals(new TextSourceMap(string, expectedSourceMapping))); -        } -    } -} - -function testDistributeFurigana() { -    const data = [ -        [ -            ['有り難う', 'ありがとう'], -            [ -                {text: '有', reading: 'あ'}, -                {text: 'り', reading: ''}, -                {text: '難', reading: 'がと'}, -                {text: 'う', reading: ''} -            ] -        ], -        [ -            ['方々', 'かたがた'], -            [ -                {text: '方々', reading: 'かたがた'} -            ] -        ], -        [ -            ['お祝い', 'おいわい'], -            [ -                {text: 'お', reading: ''}, -                {text: '祝', reading: 'いわ'}, -                {text: 'い', reading: ''} -            ] -        ], -        [ -            ['美味しい', 'おいしい'], -            [ -                {text: '美味', reading: 'おい'}, -                {text: 'しい', reading: ''} -            ] -        ], -        [ -            ['食べ物', 'たべもの'], -            [ -                {text: '食', reading: 'た'}, -                {text: 'べ', reading: ''}, -                {text: '物', reading: 'もの'} -            ] -        ], -        [ -            ['試し切り', 'ためしぎり'], -            [ -                {text: '試', reading: 'ため'}, -                {text: 'し', reading: ''}, -                {text: '切', reading: 'ぎ'}, -                {text: 'り', reading: ''} -            ] -        ], -        // Ambiguous -        [ -            ['飼い犬', 'かいいぬ'], -            [ -                {text: '飼い犬', reading: 'かいいぬ'} -            ] -        ], -        [ -            ['長い間', 'ながいあいだ'], -            [ -                {text: '長い間', reading: 'ながいあいだ'} -            ] -        ], -        // Same/empty reading -        [ -            ['飼い犬', ''], -            [ -                {text: '飼い犬', reading: ''} -            ] -        ], -        [ -            ['かいいぬ', 'かいいぬ'], -            [ -                {text: 'かいいぬ', reading: ''} -            ] -        ], -        [ -            ['かいぬ', 'かいぬ'], -            [ -                {text: 'かいぬ', reading: ''} -            ] -        ], -        // Misc -        [ -            ['月', 'か'], -            [ -                {text: '月', reading: 'か'} -            ] -        ], -        [ -            ['月', 'カ'], -            [ -                {text: '月', reading: 'カ'} -            ] -        ], -        // Mismatched kana readings -        [ -            ['有り難う', 'アリガトウ'], -            [ -                {text: '有', reading: 'ア'}, -                {text: 'り', reading: 'リ'}, -                {text: '難', reading: 'ガト'}, -                {text: 'う', reading: 'ウ'} -            ] -        ], -        [ -            ['ありがとう', 'アリガトウ'], -            [ -                {text: 'ありがとう', reading: 'アリガトウ'} -            ] -        ], -        // Mismatched kana readings (real examples) -        [ -            ['カ月', 'かげつ'], -            [ -                {text: 'カ', reading: 'か'}, -                {text: '月', reading: 'げつ'} -            ] -        ], -        [ -            ['序ノ口', 'じょのくち'], -            [ -                {text: '序', reading: 'じょ'}, -                {text: 'ノ', reading: 'の'}, -                {text: '口', reading: 'くち'} -            ] -        ], -        [ -            ['スズメの涙', 'すずめのなみだ'], -            [ -                {text: 'スズメ', reading: 'すずめ'}, -                {text: 'の', reading: ''}, -                {text: '涙', reading: 'なみだ'} -            ] -        ], -        [ -            ['二カ所', 'にかしょ'], -            [ -                {text: '二', reading: 'に'}, -                {text: 'カ', reading: 'か'}, -                {text: '所', reading: 'しょ'} -            ] -        ], -        [ -            ['八ツ橋', 'やつはし'], -            [ -                {text: '八', reading: 'や'}, -                {text: 'ツ', reading: 'つ'}, -                {text: '橋', reading: 'はし'} -            ] -        ], -        [ -            ['八ツ橋', 'やつはし'], -            [ -                {text: '八', reading: 'や'}, -                {text: 'ツ', reading: 'つ'}, -                {text: '橋', reading: 'はし'} -            ] -        ], -        [ -            ['一カ月', 'いっかげつ'], -            [ -                {text: '一', reading: 'いっ'}, -                {text: 'カ', reading: 'か'}, -                {text: '月', reading: 'げつ'} -            ] -        ], -        [ -            ['一カ所', 'いっかしょ'], -            [ -                {text: '一', reading: 'いっ'}, -                {text: 'カ', reading: 'か'}, -                {text: '所', reading: 'しょ'} -            ] -        ], -        [ -            ['カ所', 'かしょ'], -            [ -                {text: 'カ', reading: 'か'}, -                {text: '所', reading: 'しょ'} -            ] -        ], -        [ -            ['数カ月', 'すうかげつ'], -            [ -                {text: '数', reading: 'すう'}, -                {text: 'カ', reading: 'か'}, -                {text: '月', reading: 'げつ'} -            ] -        ], -        [ -            ['くノ一', 'くのいち'], -            [ -                {text: 'く', reading: ''}, -                {text: 'ノ', reading: 'の'}, -                {text: '一', reading: 'いち'} -            ] -        ], -        [ -            ['くノ一', 'くのいち'], -            [ -                {text: 'く', reading: ''}, -                {text: 'ノ', reading: 'の'}, -                {text: '一', reading: 'いち'} -            ] -        ], -        [ -            ['数カ国', 'すうかこく'], -            [ -                {text: '数', reading: 'すう'}, -                {text: 'カ', reading: 'か'}, -                {text: '国', reading: 'こく'} -            ] -        ], -        [ -            ['数カ所', 'すうかしょ'], -            [ -                {text: '数', reading: 'すう'}, -                {text: 'カ', reading: 'か'}, -                {text: '所', reading: 'しょ'} -            ] -        ], -        [ -            ['壇ノ浦の戦い', 'だんのうらのたたかい'], -            [ -                {text: '壇', reading: 'だん'}, -                {text: 'ノ', reading: 'の'}, -                {text: '浦', reading: 'うら'}, -                {text: 'の', reading: ''}, -                {text: '戦', reading: 'たたか'}, -                {text: 'い', reading: ''} -            ] -        ], -        [ -            ['壇ノ浦の戦', 'だんのうらのたたかい'], -            [ -                {text: '壇', reading: 'だん'}, -                {text: 'ノ', reading: 'の'}, -                {text: '浦', reading: 'うら'}, -                {text: 'の', reading: ''}, -                {text: '戦', reading: 'たたかい'} -            ] -        ], -        [ -            ['序ノ口格', 'じょのくちかく'], -            [ -                {text: '序', reading: 'じょ'}, -                {text: 'ノ', reading: 'の'}, -                {text: '口格', reading: 'くちかく'} -            ] -        ], -        [ -            ['二カ国語', 'にかこくご'], -            [ -                {text: '二', reading: 'に'}, -                {text: 'カ', reading: 'か'}, -                {text: '国語', reading: 'こくご'} -            ] -        ], -        [ -            ['カ国', 'かこく'], -            [ -                {text: 'カ', reading: 'か'}, -                {text: '国', reading: 'こく'} -            ] -        ], -        [ -            ['カ国語', 'かこくご'], -            [ -                {text: 'カ', reading: 'か'}, -                {text: '国語', reading: 'こくご'} -            ] -        ], -        [ -            ['壇ノ浦の合戦', 'だんのうらのかっせん'], -            [ -                {text: '壇', reading: 'だん'}, -                {text: 'ノ', reading: 'の'}, -                {text: '浦', reading: 'うら'}, -                {text: 'の', reading: ''}, -                {text: '合戦', reading: 'かっせん'} -            ] -        ], -        [ -            ['一タ偏', 'いちたへん'], -            [ -                {text: '一', reading: 'いち'}, -                {text: 'タ', reading: 'た'}, -                {text: '偏', reading: 'へん'} -            ] -        ], -        [ -            ['ル又', 'るまた'], -            [ -                {text: 'ル', reading: 'る'}, -                {text: '又', reading: 'また'} -            ] -        ], -        [ -            ['ノ木偏', 'のぎへん'], -            [ -                {text: 'ノ', reading: 'の'}, -                {text: '木偏', reading: 'ぎへん'} -            ] -        ], -        [ -            ['一ノ貝', 'いちのかい'], -            [ -                {text: '一', reading: 'いち'}, -                {text: 'ノ', reading: 'の'}, -                {text: '貝', reading: 'かい'} -            ] -        ], -        [ -            ['虎ノ門事件', 'とらのもんじけん'], -            [ -                {text: '虎', reading: 'とら'}, -                {text: 'ノ', reading: 'の'}, -                {text: '門事件', reading: 'もんじけん'} -            ] -        ], -        [ -            ['教育ニ関スル勅語', 'きょういくにかんするちょくご'], -            [ -                {text: '教育', reading: 'きょういく'}, -                {text: 'ニ', reading: 'に'}, -                {text: '関', reading: 'かん'}, -                {text: 'スル', reading: 'する'}, -                {text: '勅語', reading: 'ちょくご'} -            ] -        ], -        [ -            ['二カ年', 'にかねん'], -            [ -                {text: '二', reading: 'に'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['三カ年', 'さんかねん'], -            [ -                {text: '三', reading: 'さん'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['四カ年', 'よんかねん'], -            [ -                {text: '四', reading: 'よん'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['五カ年', 'ごかねん'], -            [ -                {text: '五', reading: 'ご'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['六カ年', 'ろっかねん'], -            [ -                {text: '六', reading: 'ろっ'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['七カ年', 'ななかねん'], -            [ -                {text: '七', reading: 'なな'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['八カ年', 'はちかねん'], -            [ -                {text: '八', reading: 'はち'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['九カ年', 'きゅうかねん'], -            [ -                {text: '九', reading: 'きゅう'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['十カ年', 'じゅうかねん'], -            [ -                {text: '十', reading: 'じゅう'}, -                {text: 'カ', reading: 'か'}, -                {text: '年', reading: 'ねん'} -            ] -        ], -        [ -            ['鏡ノ間', 'かがみのま'], -            [ -                {text: '鏡', reading: 'かがみ'}, -                {text: 'ノ', reading: 'の'}, -                {text: '間', reading: 'ま'} -            ] -        ], -        [ -            ['鏡ノ間', 'かがみのま'], -            [ -                {text: '鏡', reading: 'かがみ'}, -                {text: 'ノ', reading: 'の'}, -                {text: '間', reading: 'ま'} -            ] -        ], -        [ -            ['ページ違反', 'ぺーじいはん'], -            [ -                {text: 'ペ', reading: 'ぺ'}, -                {text: 'ー', reading: ''}, -                {text: 'ジ', reading: 'じ'}, -                {text: '違反', reading: 'いはん'} -            ] -        ], -        // Mismatched kana -        [ -            ['サボる', 'サボル'], -            [ -                {text: 'サボ', reading: ''}, -                {text: 'る', reading: 'ル'} -            ] -        ], -        // Reading starts with term, but has remainder characters -        [ -            ['シック', 'シック・ビルしょうこうぐん'], -            [ -                {text: 'シック', reading: 'シック・ビルしょうこうぐん'} -            ] -        ], -        // Kanji distribution tests -        [ -            ['逸らす', 'そらす'], -            [ -                {text: '逸', reading: 'そ'}, -                {text: 'らす', reading: ''} -            ] -        ], -        [ -            ['逸らす', 'そらす'], -            [ -                {text: '逸', reading: 'そ'}, -                {text: 'らす', reading: ''} -            ] -        ] -    ]; - -    for (const [[term, reading], expected] of data) { -        const actual = jp.distributeFurigana(term, reading); -        vm.assert.deepStrictEqual(actual, expected); -    } -} - -function testDistributeFuriganaInflected() { -    const data = [ -        [ -            ['美味しい', 'おいしい', '美味しかた'], -            [ -                {text: '美味', reading: 'おい'}, -                {text: 'しかた', reading: ''} -            ] -        ], -        [ -            ['食べる', 'たべる', '食べた'], -            [ -                {text: '食', reading: 'た'}, -                {text: 'べた', reading: ''} -            ] -        ], -        [ -            ['迄に', 'までに', 'までに'], -            [ -                {text: 'までに', reading: ''} -            ] -        ], -        [ -            ['行う', 'おこなう', 'おこなわなかった'], -            [ -                {text: 'おこなわなかった', reading: ''} -            ] -        ], -        [ -            ['いい', 'いい', 'イイ'], -            [ -                {text: 'イイ', reading: ''} -            ] -        ], -        [ -            ['否か', 'いなか', '否カ'], -            [ -                {text: '否', reading: 'いな'}, -                {text: 'カ', reading: 'か'} -            ] -        ] -    ]; - -    for (const [[term, reading, source], expected] of data) { -        const actual = jp.distributeFuriganaInflected(term, reading, source); -        vm.assert.deepStrictEqual(actual, expected); -    } -} - -function testCollapseEmphaticSequences() { -    const data = [ -        [['かこい', false], ['かこい', [1, 1, 1]]], -        [['かこい', true], ['かこい', [1, 1, 1]]], -        [['かっこい', false], ['かっこい', [1, 1, 1, 1]]], -        [['かっこい', true], ['かこい', [2, 1, 1]]], -        [['かっっこい', false], ['かっこい', [1, 2, 1, 1]]], -        [['かっっこい', true], ['かこい', [3, 1, 1]]], -        [['かっっっこい', false], ['かっこい', [1, 3, 1, 1]]], -        [['かっっっこい', true], ['かこい', [4, 1, 1]]], - -        [['こい', false], ['こい', [1, 1]]], -        [['こい', true], ['こい', [1, 1]]], -        [['っこい', false], ['っこい', [1, 1, 1]]], -        [['っこい', true], ['こい', [2, 1]]], -        [['っっこい', false], ['っこい', [2, 1, 1]]], -        [['っっこい', true], ['こい', [3, 1]]], -        [['っっっこい', false], ['っこい', [3, 1, 1]]], -        [['っっっこい', true], ['こい', [4, 1]]], - -        [['すごい', false], ['すごい', [1, 1, 1]]], -        [['すごい', true], ['すごい', [1, 1, 1]]], -        [['すごーい', false], ['すごーい', [1, 1, 1, 1]]], -        [['すごーい', true], ['すごい', [1, 2, 1]]], -        [['すごーーい', false], ['すごーい', [1, 1, 2, 1]]], -        [['すごーーい', true], ['すごい', [1, 3, 1]]], -        [['すっごーい', false], ['すっごーい', [1, 1, 1, 1, 1]]], -        [['すっごーい', true], ['すごい', [2, 2, 1]]], -        [['すっっごーーい', false], ['すっごーい', [1, 2, 1, 2, 1]]], -        [['すっっごーーい', true], ['すごい', [3, 3, 1]]], - -        [['', false], ['', []]], -        [['', true], ['', []]], -        [['っ', false], ['っ', [1]]], -        [['っ', true], ['', [1]]], -        [['っっ', false], ['っ', [2]]], -        [['っっ', true], ['', [2]]], -        [['っっっ', false], ['っ', [3]]], -        [['っっっ', true], ['', [3]]] -    ]; - -    for (const [[text, fullCollapse], [expected, expectedSourceMapping]] of data) { -        const sourceMap = new TextSourceMap(text); -        const actual1 = jp.collapseEmphaticSequences(text, fullCollapse, null); -        const actual2 = jp.collapseEmphaticSequences(text, fullCollapse, sourceMap); -        assert.strictEqual(actual1, expected); -        assert.strictEqual(actual2, expected); -        if (typeof expectedSourceMapping !== 'undefined') { -            assert.ok(sourceMap.equals(new TextSourceMap(text, expectedSourceMapping))); -        } -    } -} - -function testIsMoraPitchHigh() { -    const data = [ -        [[0, 0], false], -        [[1, 0], true], -        [[2, 0], true], -        [[3, 0], true], - -        [[0, 1], true], -        [[1, 1], false], -        [[2, 1], false], -        [[3, 1], false], - -        [[0, 2], false], -        [[1, 2], true], -        [[2, 2], false], -        [[3, 2], false], - -        [[0, 3], false], -        [[1, 3], true], -        [[2, 3], true], -        [[3, 3], false], - -        [[0, 4], false], -        [[1, 4], true], -        [[2, 4], true], -        [[3, 4], true] -    ]; - -    for (const [[moraIndex, pitchAccentDownstepPosition], expected] of data) { -        const actual = jp.isMoraPitchHigh(moraIndex, pitchAccentDownstepPosition); -        assert.strictEqual(actual, expected); -    } -} - -function testGetKanaMorae() { -    const data = [ -        ['かこ', ['か', 'こ']], -        ['かっこ', ['か', 'っ', 'こ']], -        ['カコ', ['カ', 'コ']], -        ['カッコ', ['カ', 'ッ', 'コ']], -        ['コート', ['コ', 'ー', 'ト']], -        ['ちゃんと', ['ちゃ', 'ん', 'と']], -        ['とうきょう', ['と', 'う', 'きょ', 'う']], -        ['ぎゅう', ['ぎゅ', 'う']], -        ['ディスコ', ['ディ', 'ス', 'コ']] -    ]; - -    for (const [text, expected] of data) { -        const actual = jp.getKanaMorae(text); -        vm.assert.deepStrictEqual(actual, expected); -    } -} - - -function main() { -    testIsCodePointKanji(); -    testIsCodePointKana(); -    testIsCodePointJapanese(); -    testIsStringEntirelyKana(); -    testIsStringPartiallyJapanese(); -    testConvertKatakanaToHiragana(); -    testConvertHiraganaToKatakana(); -    testConvertToRomaji(); -    testConvertNumericToFullWidth(); -    testConvertHalfWidthKanaToFullWidth(); -    testConvertAlphabeticToKana(); -    testDistributeFurigana(); -    testDistributeFuriganaInflected(); -    testCollapseEmphaticSequences(); -    testIsMoraPitchHigh(); -    testGetKanaMorae(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-json-schema.js b/test/test-json-schema.js deleted file mode 100644 index 35ecc5e9..00000000 --- a/test/test-json-schema.js +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM(); -vm.execute([ -    'js/core.js', -    'js/general/cache-map.js', -    'js/data/json-schema.js' -]); -const JsonSchema = vm.get('JsonSchema'); - - -function schemaValidate(schema, value) { -    return new JsonSchema(schema).isValid(value); -} - -function getValidValueOrDefault(schema, value) { -    return new JsonSchema(schema).getValidValueOrDefault(value); -} - -function createProxy(schema, value) { -    return new JsonSchema(schema).createProxy(value); -} - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} - - -function testValidate1() { -    const schema = { -        allOf: [ -            { -                type: 'number' -            }, -            { -                anyOf: [ -                    {minimum: 10, maximum: 100}, -                    {minimum: -100, maximum: -10} -                ] -            }, -            { -                oneOf: [ -                    {multipleOf: 3}, -                    {multipleOf: 5} -                ] -            }, -            { -                not: [ -                    {multipleOf: 20} -                ] -            } -        ] -    }; - -    const jsValidate = (value) => { -        return ( -            typeof value === 'number' && -            ( -                (value >= 10 && value <= 100) || -                (value >= -100 && value <= -10) -            ) && -            ( -                ( -                    (value % 3) === 0 || -                    (value % 5) === 0 -                ) && -                (value % 15) !== 0 -            ) && -            (value % 20) !== 0 -        ); -    }; - -    for (let i = -111; i <= 111; i++) { -        const actual = schemaValidate(schema, i); -        const expected = jsValidate(i); -        assert.strictEqual(actual, expected); -    } -} - -function testValidate2() { -    const data = [ -        // String tests -        { -            schema: { -                type: 'string' -            }, -            inputs: [ -                {expected: false, value: null}, -                {expected: false, value: void 0}, -                {expected: false, value: 0}, -                {expected: false, value: {}}, -                {expected: false, value: []}, -                {expected: true,  value: ''} -            ] -        }, -        { -            schema: { -                type: 'string', -                minLength: 2 -            }, -            inputs: [ -                {expected: false, value: ''}, -                {expected: false,  value: '1'}, -                {expected: true,  value: '12'}, -                {expected: true,  value: '123'} -            ] -        }, -        { -            schema: { -                type: 'string', -                maxLength: 2 -            }, -            inputs: [ -                {expected: true,  value: ''}, -                {expected: true,  value: '1'}, -                {expected: true,  value: '12'}, -                {expected: false, value: '123'} -            ] -        }, -        { -            schema: { -                type: 'string', -                pattern: 'test' -            }, -            inputs: [ -                {expected: false, value: ''}, -                {expected: true,  value: 'test'}, -                {expected: false, value: 'TEST'}, -                {expected: true,  value: 'ABCtestDEF'}, -                {expected: false, value: 'ABCTESTDEF'} -            ] -        }, -        { -            schema: { -                type: 'string', -                pattern: '^test$' -            }, -            inputs: [ -                {expected: false, value: ''}, -                {expected: true,  value: 'test'}, -                {expected: false, value: 'TEST'}, -                {expected: false, value: 'ABCtestDEF'}, -                {expected: false, value: 'ABCTESTDEF'} -            ] -        }, -        { -            schema: { -                type: 'string', -                pattern: '^test$', -                patternFlags: 'i' -            }, -            inputs: [ -                {expected: false, value: ''}, -                {expected: true,  value: 'test'}, -                {expected: true,  value: 'TEST'}, -                {expected: false, value: 'ABCtestDEF'}, -                {expected: false, value: 'ABCTESTDEF'} -            ] -        }, -        { -            schema: { -                type: 'string', -                pattern: '*' -            }, -            inputs: [ -                {expected: false, value: ''} -            ] -        }, -        { -            schema: { -                type: 'string', -                pattern: '.', -                patternFlags: '?' -            }, -            inputs: [ -                {expected: false, value: ''} -            ] -        }, - -        // Const tests -        { -            schema: { -                const: 32 -            }, -            inputs: [ -                {expected: true,  value: 32}, -                {expected: false, value: 0}, -                {expected: false, value: '32'}, -                {expected: false, value: null}, -                {expected: false, value: {a: 'b'}}, -                {expected: false, value: [1, 2, 3]} -            ] -        }, -        { -            schema: { -                const: '32' -            }, -            inputs: [ -                {expected: false, value: 32}, -                {expected: false, value: 0}, -                {expected: true,  value: '32'}, -                {expected: false, value: null}, -                {expected: false, value: {a: 'b'}}, -                {expected: false, value: [1, 2, 3]} -            ] -        }, -        { -            schema: { -                const: null -            }, -            inputs: [ -                {expected: false, value: 32}, -                {expected: false, value: 0}, -                {expected: false, value: '32'}, -                {expected: true,  value: null}, -                {expected: false, value: {a: 'b'}}, -                {expected: false, value: [1, 2, 3]} -            ] -        }, -        { -            schema: { -                const: {a: 'b'} -            }, -            inputs: [ -                {expected: false, value: 32}, -                {expected: false, value: 0}, -                {expected: false, value: '32'}, -                {expected: false, value: null}, -                {expected: false, value: {a: 'b'}}, -                {expected: false, value: [1, 2, 3]} -            ] -        }, -        { -            schema: { -                const: [1, 2, 3] -            }, -            inputs: [ -                {expected: false, value: 32}, -                {expected: false, value: 0}, -                {expected: false,  value: '32'}, -                {expected: false, value: null}, -                {expected: false, value: {a: 'b'}}, -                {expected: false, value: [1, 2, 3]} -            ] -        }, - -        // Array contains tests -        { -            schema: { -                type: 'array', -                contains: {const: 32} -            }, -            inputs: [ -                {expected: false, value: []}, -                {expected: true,  value: [32]}, -                {expected: true,  value: [1, 32]}, -                {expected: true,  value: [1, 32, 1]}, -                {expected: false, value: [33]}, -                {expected: false, value: [1, 33]}, -                {expected: false, value: [1, 33, 1]} -            ] -        }, - -        // Number limits tests -        { -            schema: { -                type: 'number', -                minimum: 0 -            }, -            inputs: [ -                {expected: false, value: -1}, -                {expected: true,  value: 0}, -                {expected: true,  value: 1} -            ] -        }, -        { -            schema: { -                type: 'number', -                exclusiveMinimum: 0 -            }, -            inputs: [ -                {expected: false, value: -1}, -                {expected: false, value: 0}, -                {expected: true,  value: 1} -            ] -        }, -        { -            schema: { -                type: 'number', -                maximum: 0 -            }, -            inputs: [ -                {expected: true,  value: -1}, -                {expected: true,  value: 0}, -                {expected: false, value: 1} -            ] -        }, -        { -            schema: { -                type: 'number', -                exclusiveMaximum: 0 -            }, -            inputs: [ -                {expected: true,  value: -1}, -                {expected: false, value: 0}, -                {expected: false, value: 1} -            ] -        }, - -        // Integer limits tests -        { -            schema: { -                type: 'integer', -                minimum: 0 -            }, -            inputs: [ -                {expected: false, value: -1}, -                {expected: true,  value: 0}, -                {expected: true,  value: 1} -            ] -        }, -        { -            schema: { -                type: 'integer', -                exclusiveMinimum: 0 -            }, -            inputs: [ -                {expected: false, value: -1}, -                {expected: false, value: 0}, -                {expected: true,  value: 1} -            ] -        }, -        { -            schema: { -                type: 'integer', -                maximum: 0 -            }, -            inputs: [ -                {expected: true,  value: -1}, -                {expected: true,  value: 0}, -                {expected: false, value: 1} -            ] -        }, -        { -            schema: { -                type: 'integer', -                exclusiveMaximum: 0 -            }, -            inputs: [ -                {expected: true,  value: -1}, -                {expected: false, value: 0}, -                {expected: false, value: 1} -            ] -        }, -        { -            schema: { -                type: 'integer', -                multipleOf: 2 -            }, -            inputs: [ -                {expected: true,  value: -2}, -                {expected: false, value: -1}, -                {expected: true,  value: 0}, -                {expected: false, value: 1}, -                {expected: true,  value: 2} -            ] -        }, - -        // Numeric type tests -        { -            schema: { -                type: 'number' -            }, -            inputs: [ -                {expected: true,  value: 0}, -                {expected: true,  value: 0.5}, -                {expected: true,  value: 1}, -                {expected: false, value: '0'}, -                {expected: false, value: null}, -                {expected: false, value: []}, -                {expected: false, value: {}} -            ] -        }, -        { -            schema: { -                type: 'integer' -            }, -            inputs: [ -                {expected: true,  value: 0}, -                {expected: false, value: 0.5}, -                {expected: true,  value: 1}, -                {expected: false, value: '0'}, -                {expected: false, value: null}, -                {expected: false, value: []}, -                {expected: false, value: {}} -            ] -        }, - -        // Reference tests -        { -            schema: { -                definitions: { -                    example: { -                        type: 'number' -                    } -                }, -                $ref: '#/definitions/example' -            }, -            inputs: [ -                {expected: true,  value: 0}, -                {expected: true,  value: 0.5}, -                {expected: true,  value: 1}, -                {expected: false, value: '0'}, -                {expected: false, value: null}, -                {expected: false, value: []}, -                {expected: false, value: {}} -            ] -        }, -        { -            schema: { -                definitions: { -                    example: { -                        type: 'integer' -                    } -                }, -                $ref: '#/definitions/example' -            }, -            inputs: [ -                {expected: true,  value: 0}, -                {expected: false, value: 0.5}, -                {expected: true,  value: 1}, -                {expected: false, value: '0'}, -                {expected: false, value: null}, -                {expected: false, value: []}, -                {expected: false, value: {}} -            ] -        }, -        { -            schema: { -                definitions: { -                    example: { -                        type: 'object', -                        additionalProperties: false, -                        properties: { -                            test: { -                                $ref: '#/definitions/example' -                            } -                        } -                    } -                }, -                $ref: '#/definitions/example' -            }, -            inputs: [ -                {expected: false, value: 0}, -                {expected: false, value: 0.5}, -                {expected: false, value: 1}, -                {expected: false, value: '0'}, -                {expected: false, value: null}, -                {expected: false, value: []}, -                {expected: true,  value: {}}, -                {expected: false, value: {test: 0}}, -                {expected: false, value: {test: 0.5}}, -                {expected: false, value: {test: 1}}, -                {expected: false, value: {test: '0'}}, -                {expected: false, value: {test: null}}, -                {expected: false, value: {test: []}}, -                {expected: true,  value: {test: {}}}, -                {expected: true,  value: {test: {test: {}}}}, -                {expected: true,  value: {test: {test: {test: {}}}}} -            ] -        } -    ]; - -    for (const {schema, inputs} of data) { -        for (const {expected, value} of inputs) { -            const actual = schemaValidate(schema, value); -            assert.strictEqual(actual, expected); -        } -    } -} - - -function testGetValidValueOrDefault1() { -    const data = [ -        // Test value defaulting on objects with additionalProperties=false -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                }, -                additionalProperties: false -            }, -            inputs: [ -                [ -                    void 0, -                    {test: 'default'} -                ], -                [ -                    null, -                    {test: 'default'} -                ], -                [ -                    0, -                    {test: 'default'} -                ], -                [ -                    '', -                    {test: 'default'} -                ], -                [ -                    [], -                    {test: 'default'} -                ], -                [ -                    {}, -                    {test: 'default'} -                ], -                [ -                    {test: 'value'}, -                    {test: 'value'} -                ], -                [ -                    {test2: 'value2'}, -                    {test: 'default'} -                ], -                [ -                    {test: 'value', test2: 'value2'}, -                    {test: 'value'} -                ] -            ] -        }, - -        // Test value defaulting on objects with additionalProperties=true -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                }, -                additionalProperties: true -            }, -            inputs: [ -                [ -                    {}, -                    {test: 'default'} -                ], -                [ -                    {test: 'value'}, -                    {test: 'value'} -                ], -                [ -                    {test2: 'value2'}, -                    {test: 'default', test2: 'value2'} -                ], -                [ -                    {test: 'value', test2: 'value2'}, -                    {test: 'value', test2: 'value2'} -                ] -            ] -        }, - -        // Test value defaulting on objects with additionalProperties={schema} -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                }, -                additionalProperties: { -                    type: 'number', -                    default: 10 -                } -            }, -            inputs: [ -                [ -                    {}, -                    {test: 'default'} -                ], -                [ -                    {test: 'value'}, -                    {test: 'value'} -                ], -                [ -                    {test2: 'value2'}, -                    {test: 'default', test2: 10} -                ], -                [ -                    {test: 'value', test2: 'value2'}, -                    {test: 'value', test2: 10} -                ], -                [ -                    {test2: 2}, -                    {test: 'default', test2: 2} -                ], -                [ -                    {test: 'value', test2: 2}, -                    {test: 'value', test2: 2} -                ], -                [ -                    {test: 'value', test2: 2, test3: null}, -                    {test: 'value', test2: 2, test3: 10} -                ], -                [ -                    {test: 'value', test2: 2, test3: void 0}, -                    {test: 'value', test2: 2, test3: 10} -                ] -            ] -        }, - -        // Test value defaulting where hasOwnProperty is false -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                } -            }, -            inputs: [ -                [ -                    {}, -                    {test: 'default'} -                ], -                [ -                    {test: 'value'}, -                    {test: 'value'} -                ], -                [ -                    Object.create({test: 'value'}), -                    {test: 'default'} -                ] -            ] -        }, -        { -            schema: { -                type: 'object', -                required: ['toString'], -                properties: { -                    toString: { -                        type: 'string', -                        default: 'default' -                    } -                } -            }, -            inputs: [ -                [ -                    {}, -                    {toString: 'default'} -                ], -                [ -                    {toString: 'value'}, -                    {toString: 'value'} -                ], -                [ -                    Object.create({toString: 'value'}), -                    {toString: 'default'} -                ] -            ] -        }, - -        // Test enum -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'string', -                        default: 'value1', -                        enum: ['value1', 'value2', 'value3'] -                    } -                } -            }, -            inputs: [ -                [ -                    {test: 'value1'}, -                    {test: 'value1'} -                ], -                [ -                    {test: 'value2'}, -                    {test: 'value2'} -                ], -                [ -                    {test: 'value3'}, -                    {test: 'value3'} -                ], -                [ -                    {test: 'value4'}, -                    {test: 'value1'} -                ] -            ] -        }, - -        // Test valid vs invalid default -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'integer', -                        default: 2, -                        minimum: 1 -                    } -                } -            }, -            inputs: [ -                [ -                    {test: -1}, -                    {test: 2} -                ] -            ] -        }, -        { -            schema: { -                type: 'object', -                required: ['test'], -                properties: { -                    test: { -                        type: 'integer', -                        default: 1, -                        minimum: 2 -                    } -                } -            }, -            inputs: [ -                [ -                    {test: -1}, -                    {test: -1} -                ] -            ] -        }, - -        // Test references -        { -            schema: { -                definitions: { -                    example: { -                        type: 'number', -                        default: 0 -                    } -                }, -                $ref: '#/definitions/example' -            }, -            inputs: [ -                [ -                    1, -                    1 -                ], -                [ -                    null, -                    0 -                ], -                [ -                    'test', -                    0 -                ], -                [ -                    {test: 'value'}, -                    0 -                ] -            ] -        }, -        { -            schema: { -                definitions: { -                    example: { -                        type: 'object', -                        additionalProperties: false, -                        properties: { -                            test: { -                                $ref: '#/definitions/example' -                            } -                        } -                    } -                }, -                $ref: '#/definitions/example' -            }, -            inputs: [ -                [ -                    1, -                    {} -                ], -                [ -                    null, -                    {} -                ], -                [ -                    'test', -                    {} -                ], -                [ -                    {}, -                    {} -                ], -                [ -                    {test: {}}, -                    {test: {}} -                ], -                [ -                    {test: 'value'}, -                    {test: {}} -                ], -                [ -                    {test: {test: {}}}, -                    {test: {test: {}}} -                ] -            ] -        } -    ]; - -    for (const {schema, inputs} of data) { -        for (const [value, expected] of inputs) { -            const actual = getValidValueOrDefault(schema, value); -            vm.assert.deepStrictEqual(actual, expected); -        } -    } -} - - -function testProxy1() { -    const data = [ -        // Object tests -        { -            schema: { -                type: 'object', -                required: ['test'], -                additionalProperties: false, -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                } -            }, -            tests: [ -                {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, -                {error: true,  value: {test: 'default'}, action: (value) => { value.test = null; }}, -                {error: true,  value: {test: 'default'}, action: (value) => { delete value.test; }}, -                {error: true,  value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, -                {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} -            ] -        }, -        { -            schema: { -                type: 'object', -                required: ['test'], -                additionalProperties: true, -                properties: { -                    test: { -                        type: 'string', -                        default: 'default' -                    } -                } -            }, -            tests: [ -                {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, -                {error: true,  value: {test: 'default'}, action: (value) => { value.test = null; }}, -                {error: true,  value: {test: 'default'}, action: (value) => { delete value.test; }}, -                {error: false, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, -                {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} -            ] -        }, -        { -            schema: { -                type: 'object', -                required: ['test1'], -                additionalProperties: false, -                properties: { -                    test1: { -                        type: 'object', -                        required: ['test2'], -                        additionalProperties: false, -                        properties: { -                            test2: { -                                type: 'object', -                                required: ['test3'], -                                additionalProperties: false, -                                properties: { -                                    test3: { -                                        type: 'string', -                                        default: 'default' -                                    } -                                } -                            } -                        } -                    } -                } -            }, -            tests: [ -                {error: false, action: (value) => { value.test1.test2.test3 = 'string'; }}, -                {error: true,  action: (value) => { value.test1.test2.test3 = null; }}, -                {error: true,  action: (value) => { delete value.test1.test2.test3; }}, -                {error: true,  action: (value) => { value.test1.test2 = null; }}, -                {error: true,  action: (value) => { value.test1 = null; }}, -                {error: true,  action: (value) => { value.test4 = 'string'; }}, -                {error: false, action: (value) => { delete value.test4; }} -            ] -        }, - -        // Array tests -        { -            schema: { -                type: 'array', -                items: { -                    type: 'string', -                    default: 'default' -                } -            }, -            tests: [ -                {error: false, value: ['default'], action: (value) => { value[0] = 'string'; }}, -                {error: true,  value: ['default'], action: (value) => { value[0] = null; }}, -                {error: false, value: ['default'], action: (value) => { delete value[0]; }}, -                {error: false, value: ['default'], action: (value) => { value[1] = 'string'; }}, -                {error: false, value: ['default'], action: (value) => { -                    value[1] = 'string'; -                    if (value.length !== 2) { throw new Error(`Invalid length; expected=2; actual=${value.length}`); } -                    if (typeof value.push !== 'function') { throw new Error(`Invalid push; expected=function; actual=${typeof value.push}`); } -                }} -            ] -        }, - -        // Reference tests -        { -            schema: { -                definitions: { -                    example: { -                        type: 'object', -                        additionalProperties: false, -                        properties: { -                            test: { -                                $ref: '#/definitions/example' -                            } -                        } -                    } -                }, -                $ref: '#/definitions/example' -            }, -            tests: [ -                {error: false, value: {}, action: (value) => { value.test = {}; }}, -                {error: false, value: {}, action: (value) => { value.test = {}; value.test.test = {}; }}, -                {error: false, value: {}, action: (value) => { value.test = {test: {}}; }}, -                {error: true,  value: {}, action: (value) => { value.test = null; }}, -                {error: true,  value: {}, action: (value) => { value.test = 'string'; }}, -                {error: true,  value: {}, action: (value) => { value.test = {}; value.test.test = 'string'; }}, -                {error: true,  value: {}, action: (value) => { value.test = {test: 'string'}; }} -            ] -        } -    ]; - -    for (const {schema, tests} of data) { -        for (let {error, value, action} of tests) { -            if (typeof value === 'undefined') { value = getValidValueOrDefault(schema, void 0); } -            value = clone(value); -            assert.ok(schemaValidate(schema, value)); -            const valueProxy = createProxy(schema, value); -            if (error) { -                assert.throws(() => action(valueProxy)); -            } else { -                assert.doesNotThrow(() => action(valueProxy)); -            } -        } -    } -} - - -function main() { -    testValidate1(); -    testValidate2(); -    testGetValidValueOrDefault1(); -    testProxy1(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-manifest.js b/test/test-manifest.js deleted file mode 100644 index b52152de..00000000 --- a/test/test-manifest.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {ManifestUtil} = require('../dev/manifest-util'); - - -function loadManifestString() { -    const manifestPath = path.join(__dirname, '..', 'ext', 'manifest.json'); -    return fs.readFileSync(manifestPath, {encoding: 'utf8'}); -} - -function validateManifest() { -    const manifestUtil = new ManifestUtil(); -    const manifest1 = loadManifestString(); -    const manifest2 = ManifestUtil.createManifestString(manifestUtil.getManifest()); -    assert.strictEqual(manifest1, manifest2, 'Manifest data does not match.'); -} - - -function main() { -    validateManifest(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-object-property-accessor.js b/test/test-object-property-accessor.js deleted file mode 100644 index d225e893..00000000 --- a/test/test-object-property-accessor.js +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM({}); -vm.execute('js/general/object-property-accessor.js'); -const ObjectPropertyAccessor = vm.get('ObjectPropertyAccessor'); - - -function createTestObject() { -    return { -        0: null, -        value1: { -            value2: {}, -            value3: [], -            value4: null -        }, -        value5: [ -            {}, -            [], -            null -        ] -    }; -} - - -function testGet1() { -    const data = [ -        [[], (object) => object], -        [['0'], (object) => object['0']], -        [['value1'], (object) => object.value1], -        [['value1', 'value2'], (object) => object.value1.value2], -        [['value1', 'value3'], (object) => object.value1.value3], -        [['value1', 'value4'], (object) => object.value1.value4], -        [['value5'], (object) => object.value5], -        [['value5', 0], (object) => object.value5[0]], -        [['value5', 1], (object) => object.value5[1]], -        [['value5', 2], (object) => object.value5[2]] -    ]; - -    for (const [pathArray, getExpected] of data) { -        const object = createTestObject(); -        const accessor = new ObjectPropertyAccessor(object); -        const expected = getExpected(object); - -        assert.strictEqual(accessor.get(pathArray), expected); -    } -} - -function testGet2() { -    const object = createTestObject(); -    const accessor = new ObjectPropertyAccessor(object); - -    const data = [ -        [[0], 'Invalid path: [0]'], -        [['0', 'invalid'], 'Invalid path: ["0"].invalid'], -        [['invalid'], 'Invalid path: invalid'], -        [['value1', 'invalid'], 'Invalid path: value1.invalid'], -        [['value1', 'value2', 'invalid'], 'Invalid path: value1.value2.invalid'], -        [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], -        [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], -        [['value1', 'value3', 0], 'Invalid path: value1.value3[0]'], -        [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], -        [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], -        [['value5', 'length'], 'Invalid path: value5.length'], -        [['value5', 0, 'invalid'], 'Invalid path: value5[0].invalid'], -        [['value5', 0, 0], 'Invalid path: value5[0][0]'], -        [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], -        [['value5', 1, 0], 'Invalid path: value5[1][0]'], -        [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], -        [['value5', 2, 0], 'Invalid path: value5[2][0]'], -        [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], -        [['value5', 2.5], 'Invalid index'] -    ]; - -    for (const [pathArray, message] of data) { -        assert.throws(() => accessor.get(pathArray), {message}); -    } -} - - -function testSet1() { -    const testValue = {}; -    const data = [ -        ['0'], -        ['value1', 'value2'], -        ['value1', 'value3'], -        ['value1', 'value4'], -        ['value1'], -        ['value5', 0], -        ['value5', 1], -        ['value5', 2], -        ['value5'] -    ]; - -    for (const pathArray of data) { -        const object = createTestObject(); -        const accessor = new ObjectPropertyAccessor(object); - -        accessor.set(pathArray, testValue); -        assert.strictEqual(accessor.get(pathArray), testValue); -    } -} - -function testSet2() { -    const object = createTestObject(); -    const accessor = new ObjectPropertyAccessor(object); - -    const testValue = {}; -    const data = [ -        [[], 'Invalid path'], -        [[0], 'Invalid path: [0]'], -        [['0', 'invalid'], 'Invalid path: ["0"].invalid'], -        [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], -        [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], -        [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], -        [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], -        [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], -        [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], -        [['value5', 2, 0], 'Invalid path: value5[2][0]'], -        [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], -        [['value5', 2.5], 'Invalid index'] -    ]; - -    for (const [pathArray, message] of data) { -        assert.throws(() => accessor.set(pathArray, testValue), {message}); -    } -} - - -function testDelete1() { -    const hasOwn = (object, property) => Object.prototype.hasOwnProperty.call(object, property); - -    const data = [ -        [['0'], (object) => !hasOwn(object, '0')], -        [['value1', 'value2'], (object) => !hasOwn(object.value1, 'value2')], -        [['value1', 'value3'], (object) => !hasOwn(object.value1, 'value3')], -        [['value1', 'value4'], (object) => !hasOwn(object.value1, 'value4')], -        [['value1'], (object) => !hasOwn(object, 'value1')], -        [['value5'], (object) => !hasOwn(object, 'value5')] -    ]; - -    for (const [pathArray, validate] of data) { -        const object = createTestObject(); -        const accessor = new ObjectPropertyAccessor(object); - -        accessor.delete(pathArray); -        assert.ok(validate(object)); -    } -} - -function testDelete2() { -    const data = [ -        [[], 'Invalid path'], -        [[0], 'Invalid path: [0]'], -        [['0', 'invalid'], 'Invalid path: ["0"].invalid'], -        [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'], -        [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'], -        [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'], -        [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'], -        [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'], -        [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'], -        [['value5', 2, 0], 'Invalid path: value5[2][0]'], -        [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'], -        [['value5', 2.5], 'Invalid index'], -        [['value5', 0], 'Invalid type'], -        [['value5', 1], 'Invalid type'], -        [['value5', 2], 'Invalid type'] -    ]; - -    for (const [pathArray, message] of data) { -        const object = createTestObject(); -        const accessor = new ObjectPropertyAccessor(object); - -        assert.throws(() => accessor.delete(pathArray), {message}); -    } -} - - -function testSwap1() { -    const data = [ -        [['0'], true], -        [['value1', 'value2'], true], -        [['value1', 'value3'], true], -        [['value1', 'value4'], true], -        [['value1'], false], -        [['value5', 0], true], -        [['value5', 1], true], -        [['value5', 2], true], -        [['value5'], false] -    ]; - -    for (const [pathArray1, compareValues1] of data) { -        for (const [pathArray2, compareValues2] of data) { -            const object = createTestObject(); -            const accessor = new ObjectPropertyAccessor(object); - -            const value1a = accessor.get(pathArray1); -            const value2a = accessor.get(pathArray2); - -            accessor.swap(pathArray1, pathArray2); - -            if (!compareValues1 || !compareValues2) { continue; } - -            const value1b = accessor.get(pathArray1); -            const value2b = accessor.get(pathArray2); - -            assert.deepStrictEqual(value1a, value2b); -            assert.deepStrictEqual(value2a, value1b); -        } -    } -} - -function testSwap2() { -    const data = [ -        [[], [], false, 'Invalid path 1'], -        [['0'], [], false, 'Invalid path 2'], -        [[], ['0'], false, 'Invalid path 1'], -        [[0], ['0'], false, 'Invalid path 1: [0]'], -        [['0'], [0], false, 'Invalid path 2: [0]'] -    ]; - -    for (const [pathArray1, pathArray2, checkRevert, message] of data) { -        const object = createTestObject(); -        const accessor = new ObjectPropertyAccessor(object); - -        let value1a; -        let value2a; -        if (checkRevert) { -            try { -                value1a = accessor.get(pathArray1); -                value2a = accessor.get(pathArray2); -            } catch (e) { -                // NOP -            } -        } - -        assert.throws(() => accessor.swap(pathArray1, pathArray2), {message}); - -        if (!checkRevert) { continue; } - -        const value1b = accessor.get(pathArray1); -        const value2b = accessor.get(pathArray2); - -        assert.deepStrictEqual(value1a, value1b); -        assert.deepStrictEqual(value2a, value2b); -    } -} - - -function testGetPathString1() { -    const data = [ -        [[], ''], -        [[0], '[0]'], -        [['escape\\'], '["escape\\\\"]'], -        [['\'quote\''], '["\'quote\'"]'], -        [['"quote"'], '["\\"quote\\""]'], -        [['part1', 'part2'], 'part1.part2'], -        [['part1', 'part2', 3], 'part1.part2[3]'], -        [['part1', 'part2', '3'], 'part1.part2["3"]'], -        [['part1', 'part2', '3part'], 'part1.part2["3part"]'], -        [['part1', 'part2', '3part', 'part4'], 'part1.part2["3part"].part4'], -        [['part1', 'part2', '3part', '4part'], 'part1.part2["3part"]["4part"]'] -    ]; - -    for (const [pathArray, expected] of data) { -        assert.strictEqual(ObjectPropertyAccessor.getPathString(pathArray), expected); -    } -} - -function testGetPathString2() { -    const data = [ -        [[1.5], 'Invalid index'], -        [[null], 'Invalid type: object'] -    ]; - -    for (const [pathArray, message] of data) { -        assert.throws(() => ObjectPropertyAccessor.getPathString(pathArray), {message}); -    } -} - - -function testGetPathArray1() { -    const data = [ -        ['', []], -        ['[0]', [0]], -        ['["escape\\\\"]', ['escape\\']], -        ['["\'quote\'"]', ['\'quote\'']], -        ['["\\"quote\\""]', ['"quote"']], -        ['part1.part2', ['part1', 'part2']], -        ['part1.part2[3]', ['part1', 'part2', 3]], -        ['part1.part2["3"]', ['part1', 'part2', '3']], -        ['part1.part2[\'3\']', ['part1', 'part2', '3']], -        ['part1.part2["3part"]', ['part1', 'part2', '3part']], -        ['part1.part2[\'3part\']', ['part1', 'part2', '3part']], -        ['part1.part2["3part"].part4', ['part1', 'part2', '3part', 'part4']], -        ['part1.part2[\'3part\'].part4', ['part1', 'part2', '3part', 'part4']], -        ['part1.part2["3part"]["4part"]', ['part1', 'part2', '3part', '4part']], -        ['part1.part2[\'3part\'][\'4part\']', ['part1', 'part2', '3part', '4part']] -    ]; - -    for (const [pathString, expected] of data) { -        vm.assert.deepStrictEqual(ObjectPropertyAccessor.getPathArray(pathString), expected); -    } -} - -function testGetPathArray2() { -    const data = [ -        ['?', 'Unexpected character: ?'], -        ['.', 'Unexpected character: .'], -        ['0', 'Unexpected character: 0'], -        ['part1.[0]', 'Unexpected character: ['], -        ['part1?', 'Unexpected character: ?'], -        ['[part1]', 'Unexpected character: p'], -        ['[0a]', 'Unexpected character: a'], -        ['["part1"x]', 'Unexpected character: x'], -        ['[\'part1\'x]', 'Unexpected character: x'], -        ['["part1"]x', 'Unexpected character: x'], -        ['[\'part1\']x', 'Unexpected character: x'], -        ['part1..part2', 'Unexpected character: .'], - -        ['[', 'Path not terminated correctly'], -        ['part1.', 'Path not terminated correctly'], -        ['part1[', 'Path not terminated correctly'], -        ['part1["', 'Path not terminated correctly'], -        ['part1[\'', 'Path not terminated correctly'], -        ['part1[""', 'Path not terminated correctly'], -        ['part1[\'\'', 'Path not terminated correctly'], -        ['part1[0', 'Path not terminated correctly'], -        ['part1[0].', 'Path not terminated correctly'] -    ]; - -    for (const [pathString, message] of data) { -        assert.throws(() => ObjectPropertyAccessor.getPathArray(pathString), {message}); -    } -} - - -function testHasProperty() { -    const data = [ -        [{}, 'invalid', false], -        [{}, 0, false], -        [{valid: 0}, 'valid', true], -        [{null: 0}, null, false], -        [[], 'invalid', false], -        [[], 0, false], -        [[0], 0, true], -        [[0], null, false], -        ['string', 0, false], -        ['string', 'length', false], -        ['string', null, false] -    ]; - -    for (const [object, property, expected] of data) { -        assert.strictEqual(ObjectPropertyAccessor.hasProperty(object, property), expected); -    } -} - -function testIsValidPropertyType() { -    const data = [ -        [{}, 'invalid', true], -        [{}, 0, false], -        [{valid: 0}, 'valid', true], -        [{null: 0}, null, false], -        [[], 'invalid', false], -        [[], 0, true], -        [[0], 0, true], -        [[0], null, false], -        ['string', 0, false], -        ['string', 'length', false], -        ['string', null, false] -    ]; - -    for (const [object, property, expected] of data) { -        assert.strictEqual(ObjectPropertyAccessor.isValidPropertyType(object, property), expected); -    } -} - - -function main() { -    testGet1(); -    testGet2(); -    testSet1(); -    testSet2(); -    testDelete1(); -    testDelete2(); -    testSwap1(); -    testSwap2(); -    testGetPathString1(); -    testGetPathString2(); -    testGetPathArray1(); -    testGetPathArray2(); -    testHasProperty(); -    testIsValidPropertyType(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-profile-conditions-util.js b/test/test-profile-conditions-util.js deleted file mode 100644 index d5187425..00000000 --- a/test/test-profile-conditions-util.js +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - - -const vm = new VM({}); -vm.execute([ -    'js/core.js', -    'js/general/cache-map.js', -    'js/data/json-schema.js', -    'js/background/profile-conditions-util.js' -]); -const [ProfileConditionsUtil] = vm.get(['ProfileConditionsUtil']); - - -function testNormalizeContext() { -    const data = [ -        // Empty -        { -            context: {}, -            expected: {flags: []} -        }, - -        // Domain normalization -        { -            context: {url: ''}, -            expected: {url: '', flags: []} -        }, -        { -            context: {url: 'http://example.com/'}, -            expected: {url: 'http://example.com/', domain: 'example.com', flags: []} -        }, -        { -            context: {url: 'http://example.com:1234/'}, -            expected: {url: 'http://example.com:1234/', domain: 'example.com', flags: []} -        }, -        { -            context: {url: 'http://user@example.com:1234/'}, -            expected: {url: 'http://user@example.com:1234/', domain: 'example.com', flags: []} -        } -    ]; - -    for (const {context, expected} of data) { -        const profileConditionsUtil = new ProfileConditionsUtil(); -        const actual = profileConditionsUtil.normalizeContext(context); -        vm.assert.deepStrictEqual(actual, expected); -    } -} - -function testSchemas() { -    const data = [ -        // Empty -        { -            conditionGroups: [], -            expectedSchema: {}, -            inputs: [ -                {expected: true, context: {url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                {conditions: []} -            ], -            expectedSchema: {}, -            inputs: [ -                {expected: true, context: {url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                {conditions: []}, -                {conditions: []} -            ], -            expectedSchema: {}, -            inputs: [ -                {expected: true, context: {url: 'http://example.com/'}} -            ] -        }, - -        // popupLevel tests -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'equal', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    depth: {const: 0} -                }, -                required: ['depth'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 1, url: 'http://example.com/'}}, -                {expected: false, context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'notEqual', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                not: [ -                    { -                        properties: { -                            depth: {const: 0} -                        }, -                        required: ['depth'] -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'lessThan', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    depth: { -                        type: 'number', -                        exclusiveMaximum: 0 -                    } -                }, -                required: ['depth'] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'greaterThan', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    depth: { -                        type: 'number', -                        exclusiveMinimum: 0 -                    } -                }, -                required: ['depth'] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: false, context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'lessThanOrEqual', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    depth: { -                        type: 'number', -                        maximum: 0 -                    } -                }, -                required: ['depth'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'greaterThanOrEqual', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    depth: { -                        type: 'number', -                        minimum: 0 -                    } -                }, -                required: ['depth'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: false, context: {depth: -1, url: 'http://example.com/'}} -            ] -        }, - -        // url tests -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'url', -                            operator: 'matchDomain', -                            value: 'example.com' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    domain: { -                        oneOf: [ -                            {const: 'example.com'} -                        ] -                    } -                }, -                required: ['domain'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 0, url: 'http://example1.com/'}}, -                {expected: false, context: {depth: 0, url: 'http://example2.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com:1234/'}}, -                {expected: true,  context: {depth: 0, url: 'http://user@example.com:1234/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'url', -                            operator: 'matchDomain', -                            value: 'example.com, example1.com, example2.com' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    domain: { -                        oneOf: [ -                            {const: 'example.com'}, -                            {const: 'example1.com'}, -                            {const: 'example2.com'} -                        ] -                    } -                }, -                required: ['domain'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example1.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example2.com/'}}, -                {expected: false, context: {depth: 0, url: 'http://example3.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com:1234/'}}, -                {expected: true,  context: {depth: 0, url: 'http://user@example.com:1234/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'url', -                            operator: 'matchRegExp', -                            value: '^http://example\\d?\\.com/[\\w\\W]*$' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    url: { -                        type: 'string', -                        pattern: '^http://example\\d?\\.com/[\\w\\W]*$', -                        patternFlags: 'i' -                    } -                }, -                required: ['url'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example1.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example2.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example3.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/example'}}, -                {expected: false, context: {depth: 0, url: 'http://example.com:1234/'}}, -                {expected: false, context: {depth: 0, url: 'http://user@example.com:1234/'}}, -                {expected: false, context: {depth: 0, url: 'http://example-1.com/'}} -            ] -        }, - -        // modifierKeys tests -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'are', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array', -                        maxItems: 0, -                        minItems: 0 -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'are', -                            value: 'Alt, Shift' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array', -                        maxItems: 2, -                        minItems: 2, -                        allOf: [ -                            {contains: {const: 'Alt'}}, -                            {contains: {const: 'Shift'}} -                        ] -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'areNot', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                not: [ -                    { -                        properties: { -                            modifierKeys: { -                                type: 'array', -                                maxItems: 0, -                                minItems: 0 -                            } -                        }, -                        required: ['modifierKeys'] -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'areNot', -                            value: 'Alt, Shift' -                        } -                    ] -                } -            ], -            expectedSchema: { -                not: [ -                    { -                        properties: { -                            modifierKeys: { -                                type: 'array', -                                maxItems: 2, -                                minItems: 2, -                                allOf: [ -                                    {contains: {const: 'Alt'}}, -                                    {contains: {const: 'Shift'}} -                                ] -                            } -                        }, -                        required: ['modifierKeys'] -                    } -                ] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'include', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array', -                        minItems: 0 -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'include', -                            value: 'Alt, Shift' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array', -                        minItems: 2, -                        allOf: [ -                            {contains: {const: 'Alt'}}, -                            {contains: {const: 'Shift'}} -                        ] -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'notInclude', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array' -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'modifierKeys', -                            operator: 'notInclude', -                            value: 'Alt, Shift' -                        } -                    ] -                } -            ], -            expectedSchema: { -                properties: { -                    modifierKeys: { -                        type: 'array', -                        not: [ -                            {contains: {const: 'Alt'}}, -                            {contains: {const: 'Shift'}} -                        ] -                    } -                }, -                required: ['modifierKeys'] -            }, -            inputs: [ -                {expected: true,  context: {depth: 0, url: 'http://example.com/', modifierKeys: []}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift']}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['Alt', 'Shift', 'Ctrl']}} -            ] -        }, - -        // flags tests -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'are', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array', -                        maxItems: 0, -                        minItems: 0 -                    } -                } -            }, -            inputs: [ -                {expected: true,  context: {}}, -                {expected: true,  context: {flags: []}}, -                {expected: false, context: {flags: ['test1']}}, -                {expected: false, context: {flags: ['test1', 'test2']}}, -                {expected: false, context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'are', -                            value: 'test1, test2' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array', -                        maxItems: 2, -                        minItems: 2, -                        allOf: [ -                            {contains: {const: 'test1'}}, -                            {contains: {const: 'test2'}} -                        ] -                    } -                } -            }, -            inputs: [ -                {expected: false, context: {}}, -                {expected: false, context: {flags: []}}, -                {expected: false, context: {flags: ['test1']}}, -                {expected: true,  context: {flags: ['test1', 'test2']}}, -                {expected: false, context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'areNot', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                not: [ -                    { -                        required: ['flags'], -                        properties: { -                            flags: { -                                type: 'array', -                                maxItems: 0, -                                minItems: 0 -                            } -                        } -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {}}, -                {expected: false, context: {flags: []}}, -                {expected: true,  context: {flags: ['test1']}}, -                {expected: true,  context: {flags: ['test1', 'test2']}}, -                {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'areNot', -                            value: 'test1, test2' -                        } -                    ] -                } -            ], -            expectedSchema: { -                not: [ -                    { -                        required: ['flags'], -                        properties: { -                            flags: { -                                type: 'array', -                                maxItems: 2, -                                minItems: 2, -                                allOf: [ -                                    {contains: {const: 'test1'}}, -                                    {contains: {const: 'test2'}} -                                ] -                            } -                        } -                    } -                ] -            }, -            inputs: [ -                {expected: true,  context: {}}, -                {expected: true,  context: {flags: []}}, -                {expected: true,  context: {flags: ['test1']}}, -                {expected: false, context: {flags: ['test1', 'test2']}}, -                {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'include', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array', -                        minItems: 0 -                    } -                } -            }, -            inputs: [ -                {expected: true,  context: {}}, -                {expected: true,  context: {flags: []}}, -                {expected: true,  context: {flags: ['test1']}}, -                {expected: true,  context: {flags: ['test1', 'test2']}}, -                {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'include', -                            value: 'test1, test2' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array', -                        minItems: 2, -                        allOf: [ -                            {contains: {const: 'test1'}}, -                            {contains: {const: 'test2'}} -                        ] -                    } -                } -            }, -            inputs: [ -                {expected: false, context: {}}, -                {expected: false, context: {flags: []}}, -                {expected: false, context: {flags: ['test1']}}, -                {expected: true,  context: {flags: ['test1', 'test2']}}, -                {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'notInclude', -                            value: '' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array' -                    } -                } -            }, -            inputs: [ -                {expected: true,  context: {}}, -                {expected: true,  context: {flags: []}}, -                {expected: true,  context: {flags: ['test1']}}, -                {expected: true,  context: {flags: ['test1', 'test2']}}, -                {expected: true,  context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'flags', -                            operator: 'notInclude', -                            value: 'test1, test2' -                        } -                    ] -                } -            ], -            expectedSchema: { -                required: ['flags'], -                properties: { -                    flags: { -                        type: 'array', -                        not: [ -                            {contains: {const: 'test1'}}, -                            {contains: {const: 'test2'}} -                        ] -                    } -                } -            }, -            inputs: [ -                {expected: true,  context: {}}, -                {expected: true,  context: {flags: []}}, -                {expected: false, context: {flags: ['test1']}}, -                {expected: false, context: {flags: ['test1', 'test2']}}, -                {expected: false, context: {flags: ['test1', 'test2', 'test3']}} -            ] -        }, - -        // Multiple conditions tests -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'greaterThan', -                            value: '0' -                        }, -                        { -                            type: 'popupLevel', -                            operator: 'lessThan', -                            value: '3' -                        } -                    ] -                } -            ], -            expectedSchema: { -                allOf: [ -                    { -                        properties: { -                            depth: { -                                type: 'number', -                                exclusiveMinimum: 0 -                            } -                        }, -                        required: ['depth'] -                    }, -                    { -                        properties: { -                            depth: { -                                type: 'number', -                                exclusiveMaximum: 3 -                            } -                        }, -                        required: ['depth'] -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {depth: -2, url: 'http://example.com/'}}, -                {expected: false, context: {depth: -1, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 3, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'greaterThan', -                            value: '0' -                        }, -                        { -                            type: 'popupLevel', -                            operator: 'lessThan', -                            value: '3' -                        } -                    ] -                }, -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'equal', -                            value: '0' -                        } -                    ] -                } -            ], -            expectedSchema: { -                anyOf: [ -                    { -                        allOf: [ -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        exclusiveMinimum: 0 -                                    } -                                }, -                                required: ['depth'] -                            }, -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        exclusiveMaximum: 3 -                                    } -                                }, -                                required: ['depth'] -                            } -                        ] -                    }, -                    { -                        properties: { -                            depth: {const: 0} -                        }, -                        required: ['depth'] -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {depth: -2, url: 'http://example.com/'}}, -                {expected: false, context: {depth: -1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 3, url: 'http://example.com/'}} -            ] -        }, -        { -            conditionGroups: [ -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'greaterThan', -                            value: '0' -                        }, -                        { -                            type: 'popupLevel', -                            operator: 'lessThan', -                            value: '3' -                        } -                    ] -                }, -                { -                    conditions: [ -                        { -                            type: 'popupLevel', -                            operator: 'lessThanOrEqual', -                            value: '0' -                        }, -                        { -                            type: 'popupLevel', -                            operator: 'greaterThanOrEqual', -                            value: '-1' -                        } -                    ] -                } -            ], -            expectedSchema: { -                anyOf: [ -                    { -                        allOf: [ -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        exclusiveMinimum: 0 -                                    } -                                }, -                                required: ['depth'] -                            }, -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        exclusiveMaximum: 3 -                                    } -                                }, -                                required: ['depth'] -                            } -                        ] -                    }, -                    { -                        allOf: [ -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        maximum: 0 -                                    } -                                }, -                                required: ['depth'] -                            }, -                            { -                                properties: { -                                    depth: { -                                        type: 'number', -                                        minimum: -1 -                                    } -                                }, -                                required: ['depth'] -                            } -                        ] -                    } -                ] -            }, -            inputs: [ -                {expected: false, context: {depth: -2, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: -1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 0, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 1, url: 'http://example.com/'}}, -                {expected: true,  context: {depth: 2, url: 'http://example.com/'}}, -                {expected: false, context: {depth: 3, url: 'http://example.com/'}} -            ] -        } -    ]; - -    for (const {conditionGroups, expectedSchema, inputs} of data) { -        const profileConditionsUtil = new ProfileConditionsUtil(); -        const schema = profileConditionsUtil.createSchema(conditionGroups); -        if (typeof expectedSchema !== 'undefined') { -            vm.assert.deepStrictEqual(schema.schema, expectedSchema); -        } -        if (Array.isArray(inputs)) { -            for (const {expected, context} of inputs) { -                const normalizedContext = profileConditionsUtil.normalizeContext(context); -                const actual = schema.isValid(normalizedContext); -                assert.strictEqual(actual, expected); -            } -        } -    } -} - - -function main() { -    testNormalizeContext(); -    testSchemas(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-text-source-map.js b/test/test-text-source-map.js deleted file mode 100644 index dd8d3bbd..00000000 --- a/test/test-text-source-map.js +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {VM} = require('../dev/vm'); - -const vm = new VM(); -vm.execute(['js/general/text-source-map.js']); -const TextSourceMap = vm.get('TextSourceMap'); - - -function testSource() { -    const data = [ -        ['source1'], -        ['source2'], -        ['source3'] -    ]; - -    for (const [source] of data) { -        const sourceMap = new TextSourceMap(source); -        assert.strictEqual(source, sourceMap.source); -    } -} - -function testEquals() { -    const data = [ -        [['source1', null], ['source1', null], true], -        [['source2', null], ['source2', null], true], -        [['source3', null], ['source3', null], true], - -        [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', null], true], -        [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', null], true], -        [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', null], true], - -        [['source1', null], ['source1', [1, 1, 1, 1, 1, 1, 1]], true], -        [['source2', null], ['source2', [1, 1, 1, 1, 1, 1, 1]], true], -        [['source3', null], ['source3', [1, 1, 1, 1, 1, 1, 1]], true], - -        [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', [1, 1, 1, 1, 1, 1, 1]], true], -        [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', [1, 1, 1, 1, 1, 1, 1]], true], -        [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', [1, 1, 1, 1, 1, 1, 1]], true], - -        [['source1', [1, 2, 1, 3]], ['source1', [1, 2, 1, 3]], true], -        [['source2', [1, 2, 1, 3]], ['source2', [1, 2, 1, 3]], true], -        [['source3', [1, 2, 1, 3]], ['source3', [1, 2, 1, 3]], true], - -        [['source1', [1, 3, 1, 2]], ['source1', [1, 2, 1, 3]], false], -        [['source2', [1, 3, 1, 2]], ['source2', [1, 2, 1, 3]], false], -        [['source3', [1, 3, 1, 2]], ['source3', [1, 2, 1, 3]], false], - -        [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source4', [1, 1, 1, 1, 1, 1, 1]], false], -        [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source5', [1, 1, 1, 1, 1, 1, 1]], false], -        [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source6', [1, 1, 1, 1, 1, 1, 1]], false] -    ]; - -    for (const [[source1, mapping1], [source2, mapping2], expectedEquals] of data) { -        const sourceMap1 = new TextSourceMap(source1, mapping1); -        const sourceMap2 = new TextSourceMap(source2, mapping2); -        assert.ok(sourceMap1.equals(sourceMap1)); -        assert.ok(sourceMap2.equals(sourceMap2)); -        assert.strictEqual(sourceMap1.equals(sourceMap2), expectedEquals); -    } -} - -function testGetSourceLength() { -    const data = [ -        [['source', [1, 1, 1, 1, 1, 1]], 1, 1], -        [['source', [1, 1, 1, 1, 1, 1]], 2, 2], -        [['source', [1, 1, 1, 1, 1, 1]], 3, 3], -        [['source', [1, 1, 1, 1, 1, 1]], 4, 4], -        [['source', [1, 1, 1, 1, 1, 1]], 5, 5], -        [['source', [1, 1, 1, 1, 1, 1]], 6, 6], - -        [['source', [2, 2, 2]], 1, 2], -        [['source', [2, 2, 2]], 2, 4], -        [['source', [2, 2, 2]], 3, 6], - -        [['source', [3, 3]], 1, 3], -        [['source', [3, 3]], 2, 6], - -        [['source', [6, 6]], 1, 6] -    ]; - -    for (const [[source, mapping], finalLength, expectedValue] of data) { -        const sourceMap = new TextSourceMap(source, mapping); -        assert.strictEqual(sourceMap.getSourceLength(finalLength), expectedValue); -    } -} - -function testCombineInsert() { -    const data = [ -        // No operations -        [ -            ['source', null], -            ['source', [1, 1, 1, 1, 1, 1]], -            [] -        ], - -        // Combine -        [ -            ['source', null], -            ['source', [3, 1, 1, 1]], -            [ -                ['combine', 0, 2] -            ] -        ], -        [ -            ['source', null], -            ['source', [1, 1, 1, 3]], -            [ -                ['combine', 3, 2] -            ] -        ], -        [ -            ['source', null], -            ['source', [3, 3]], -            [ -                ['combine', 0, 2], -                ['combine', 1, 2] -            ] -        ], -        [ -            ['source', null], -            ['source', [3, 3]], -            [ -                ['combine', 3, 2], -                ['combine', 0, 2] -            ] -        ], - -        // Insert -        [ -            ['source', null], -            ['source', [0, 1, 1, 1, 1, 1, 1]], -            [ -                ['insert', 0, 0] -            ] -        ], -        [ -            ['source', null], -            ['source', [1, 1, 1, 1, 1, 1, 0]], -            [ -                ['insert', 6, 0] -            ] -        ], -        [ -            ['source', null], -            ['source', [0, 1, 1, 1, 1, 1, 1, 0]], -            [ -                ['insert', 0, 0], -                ['insert', 7, 0] -            ] -        ], -        [ -            ['source', null], -            ['source', [0, 1, 1, 1, 1, 1, 1, 0]], -            [ -                ['insert', 6, 0], -                ['insert', 0, 0] -            ] -        ], - -        // Mixed -        [ -            ['source', null], -            ['source', [3, 0, 3]], -            [ -                ['combine', 0, 2], -                ['insert', 1, 0], -                ['combine', 2, 2] -            ] -        ], -        [ -            ['source', null], -            ['source', [3, 0, 3]], -            [ -                ['combine', 0, 2], -                ['combine', 1, 2], -                ['insert', 1, 0] -            ] -        ], -        [ -            ['source', null], -            ['source', [3, 0, 3]], -            [ -                ['insert', 3, 0], -                ['combine', 0, 2], -                ['combine', 2, 2] -            ] -        ] -    ]; - -    for (const [[source, mapping], [expectedSource, expectedMapping], operations] of data) { -        const sourceMap = new TextSourceMap(source, mapping); -        const expectedSourceMap = new TextSourceMap(expectedSource, expectedMapping); -        for (const [operation, ...args] of operations) { -            switch (operation) { -                case 'combine': -                    sourceMap.combine(...args); -                    break; -                case 'insert': -                    sourceMap.insert(...args); -                    break; -            } -        } -        assert.ok(sourceMap.equals(expectedSourceMap)); -    } -} - - -function main() { -    testSource(); -    testEquals(); -    testGetSourceLength(); -    testCombineInsert(); -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-translator.js b/test/test-translator.js deleted file mode 100644 index 485eb665..00000000 --- a/test/test-translator.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const {testMain} = require('../dev/util'); -const {TranslatorVM} = require('../dev/translator-vm'); - - -function clone(value) { -    return JSON.parse(JSON.stringify(value)); -} - - -async function main() { -    const write = (process.argv[2] === '--write'); - -    const translatorVM = new TranslatorVM(); -    const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', 'valid-dictionary1'); -    await translatorVM.prepare(dictionaryDirectory, 'Test Dictionary 2'); - -    const testInputsFilePath = path.join(__dirname, 'data', 'translator-test-inputs.json'); -    const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'})); - -    const testResults1FilePath = path.join(__dirname, 'data', 'translator-test-results.json'); -    const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'})); -    const actualResults1 = []; - -    const testResults2FilePath = path.join(__dirname, 'data', 'translator-test-results-note-data1.json'); -    const expectedResults2 = JSON.parse(fs.readFileSync(testResults2FilePath, {encoding: 'utf8'})); -    const actualResults2 = []; - -    for (let i = 0, ii = tests.length; i < ii; ++i) { -        const test = tests[i]; -        const expected1 = expectedResults1[i]; -        const expected2 = expectedResults2[i]; -        switch (test.func) { -            case 'findTerms': -                { -                    const {name, mode, text} = test; -                    const options = translatorVM.buildOptions(optionsPresets, test.options); -                    const {dictionaryEntries, originalTextLength} = clone(await translatorVM.translator.findTerms(mode, text, options)); -                    const noteDataList = mode !== 'simple' ? clone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(clone(dictionaryEntry), mode))) : null; -                    actualResults1.push({name, originalTextLength, dictionaryEntries}); -                    actualResults2.push({name, noteDataList}); -                    if (!write) { -                        assert.deepStrictEqual(originalTextLength, expected1.originalTextLength); -                        assert.deepStrictEqual(dictionaryEntries, expected1.dictionaryEntries); -                        assert.deepStrictEqual(noteDataList, expected2.noteDataList); -                    } -                } -                break; -            case 'findKanji': -                { -                    const {name, text} = test; -                    const options = translatorVM.buildOptions(optionsPresets, test.options); -                    const dictionaryEntries = clone(await translatorVM.translator.findKanji(text, options)); -                    const noteDataList = clone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(clone(dictionaryEntry), null))); -                    actualResults1.push({name, dictionaryEntries}); -                    actualResults2.push({name, noteDataList}); -                    if (!write) { -                        assert.deepStrictEqual(dictionaryEntries, expected1.dictionaryEntries); -                        assert.deepStrictEqual(noteDataList, expected2.noteDataList); -                    } -                } -                break; -        } -    } - -    if (write) { -        // Use 2 indent instead of 4 to save a bit of file size -        fs.writeFileSync(testResults1FilePath, JSON.stringify(actualResults1, null, 2), {encoding: 'utf8'}); -        fs.writeFileSync(testResults2FilePath, JSON.stringify(actualResults2, null, 2), {encoding: 'utf8'}); -    } -} - - -if (require.main === module) { testMain(main); } diff --git a/test/test-workers.js b/test/test-workers.js deleted file mode 100644 index b4ec4d7d..00000000 --- a/test/test-workers.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2023  Yomitan Authors - * Copyright (C) 2020-2022  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/>. - */ - -const fs = require('fs'); -const path = require('path'); -const {JSDOM} = require('jsdom'); -const {VM} = require('../dev/vm'); -const assert = require('assert'); - - -class StubClass { -    prepare() { -        // NOP -    } -} - - -function loadEslint() { -    return JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.eslintrc.json'), {encoding: 'utf8'})); -} - -function filterScriptPaths(scriptPaths) { -    const extDirName = 'ext'; -    return scriptPaths.filter((src) => !src.startsWith('/lib/')).map((src) => `${extDirName}${src}`); -} - -function getAllHtmlScriptPaths(fileName) { -    const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); -    const dom = new JSDOM(domSource); -    const {window} = dom; -    const {document} = window; -    try { -        const scripts = document.querySelectorAll('script'); -        return [...scripts].map(({src}) => src); -    } finally { -        window.close(); -    } -} - -function convertBackgroundScriptsToServiceWorkerScripts(scripts) { -    // Use parse5-based SimpleDOMParser -    scripts.splice(0, 0, '/lib/parse5.js'); -    const index = scripts.indexOf('/js/dom/native-simple-dom-parser.js'); -    assert.ok(index >= 0); -    scripts[index] = '/js/dom/simple-dom-parser.js'; -} - -function getImportedScripts(scriptPath, fields) { -    const importedScripts = []; - -    const importScripts = (...scripts) => { -        importedScripts.push(...scripts); -    }; - -    const vm = new VM(Object.assign({importScripts}, fields)); -    vm.context.self = vm.context; -    vm.execute([scriptPath]); - -    return importedScripts; -} - -function testServiceWorker() { -    // Verify that sw.js scripts match background.html scripts -    const extDir = path.join(__dirname, '..', 'ext'); -    const scripts = getAllHtmlScriptPaths(path.join(extDir, 'background.html')); -    convertBackgroundScriptsToServiceWorkerScripts(scripts); -    const importedScripts = getImportedScripts('sw.js', {}); -    assert.deepStrictEqual(scripts, importedScripts); - -    // Verify that eslint config lists files correctly -    const expectedSwRulesFiles = filterScriptPaths(scripts); -    const eslintConfig = loadEslint(); -    const swRules = eslintConfig.overrides.find((item) => ( -        typeof item.env === 'object' && -        item.env !== null && -        item.env.serviceworker === true -    )); -    assert.ok(typeof swRules !== 'undefined'); -    assert.ok(Array.isArray(swRules.files)); -    assert.deepStrictEqual(swRules.files, expectedSwRulesFiles); -} - -function testWorkers() { -    testWorker( -        'js/language/dictionary-worker-main.js', -        {DictionaryWorkerHandler: StubClass} -    ); -} - -function testWorker(scriptPath, fields) { -    // Get script paths -    const scripts = getImportedScripts(scriptPath, fields); - -    // Verify that eslint config lists files correctly -    const expectedRulesFiles = filterScriptPaths(scripts); -    const expectedRulesFilesSet = new Set(expectedRulesFiles); -    const eslintConfig = loadEslint(); -    const rules = eslintConfig.overrides.find((item) => ( -        typeof item.env === 'object' && -        item.env !== null && -        item.env.worker === true -    )); -    assert.ok(typeof rules !== 'undefined'); -    assert.ok(Array.isArray(rules.files)); -    assert.deepStrictEqual(rules.files.filter((v) => expectedRulesFilesSet.has(v)), expectedRulesFiles); -} - - -function main() { -    try { -        testServiceWorker(); -        testWorkers(); -    } catch (e) { -        console.error(e); -        process.exit(-1); -        return; -    } -    process.exit(0); -} - - -if (require.main === module) { main(); } diff --git a/test/text-source-map.test.js b/test/text-source-map.test.js new file mode 100644 index 00000000..aeaba000 --- /dev/null +++ b/test/text-source-map.test.js @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {TextSourceMap} from '../ext/js/general/text-source-map.js'; + +function testSource() { +    test('Source', () => { +        const data = [ +            ['source1'], +            ['source2'], +            ['source3'] +        ]; + +        for (const [source] of data) { +            const sourceMap = new TextSourceMap(source); +            expect(source).toStrictEqual(sourceMap.source); +        } +    }); +} + +function testEquals() { +    test('Equals', () => { +        const data = [ +            [['source1', null], ['source1', null], true], +            [['source2', null], ['source2', null], true], +            [['source3', null], ['source3', null], true], + +            [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', null], true], +            [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', null], true], +            [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', null], true], + +            [['source1', null], ['source1', [1, 1, 1, 1, 1, 1, 1]], true], +            [['source2', null], ['source2', [1, 1, 1, 1, 1, 1, 1]], true], +            [['source3', null], ['source3', [1, 1, 1, 1, 1, 1, 1]], true], + +            [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', [1, 1, 1, 1, 1, 1, 1]], true], +            [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', [1, 1, 1, 1, 1, 1, 1]], true], +            [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', [1, 1, 1, 1, 1, 1, 1]], true], + +            [['source1', [1, 2, 1, 3]], ['source1', [1, 2, 1, 3]], true], +            [['source2', [1, 2, 1, 3]], ['source2', [1, 2, 1, 3]], true], +            [['source3', [1, 2, 1, 3]], ['source3', [1, 2, 1, 3]], true], + +            [['source1', [1, 3, 1, 2]], ['source1', [1, 2, 1, 3]], false], +            [['source2', [1, 3, 1, 2]], ['source2', [1, 2, 1, 3]], false], +            [['source3', [1, 3, 1, 2]], ['source3', [1, 2, 1, 3]], false], + +            [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source4', [1, 1, 1, 1, 1, 1, 1]], false], +            [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source5', [1, 1, 1, 1, 1, 1, 1]], false], +            [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source6', [1, 1, 1, 1, 1, 1, 1]], false] +        ]; + +        for (const [[source1, mapping1], [source2, mapping2], expectedEquals] of data) { +            const sourceMap1 = new TextSourceMap(source1, mapping1); +            const sourceMap2 = new TextSourceMap(source2, mapping2); +            expect(sourceMap1.equals(sourceMap1)).toBe(true); +            expect(sourceMap2.equals(sourceMap2)).toBe(true); +            expect(sourceMap1.equals(sourceMap2)).toStrictEqual(expectedEquals); +        } +    }); +} + +function testGetSourceLength() { +    test('GetSourceLength', () => { +        const data = [ +            [['source', [1, 1, 1, 1, 1, 1]], 1, 1], +            [['source', [1, 1, 1, 1, 1, 1]], 2, 2], +            [['source', [1, 1, 1, 1, 1, 1]], 3, 3], +            [['source', [1, 1, 1, 1, 1, 1]], 4, 4], +            [['source', [1, 1, 1, 1, 1, 1]], 5, 5], +            [['source', [1, 1, 1, 1, 1, 1]], 6, 6], + +            [['source', [2, 2, 2]], 1, 2], +            [['source', [2, 2, 2]], 2, 4], +            [['source', [2, 2, 2]], 3, 6], + +            [['source', [3, 3]], 1, 3], +            [['source', [3, 3]], 2, 6], + +            [['source', [6, 6]], 1, 6] +        ]; + +        for (const [[source, mapping], finalLength, expectedValue] of data) { +            const sourceMap = new TextSourceMap(source, mapping); +            expect(sourceMap.getSourceLength(finalLength)).toStrictEqual(expectedValue); +        } +    }); +} + +function testCombineInsert() { +    test('CombineInsert', () => { +        const data = [ +        // No operations +            [ +                ['source', null], +                ['source', [1, 1, 1, 1, 1, 1]], +                [] +            ], + +            // Combine +            [ +                ['source', null], +                ['source', [3, 1, 1, 1]], +                [ +                    ['combine', 0, 2] +                ] +            ], +            [ +                ['source', null], +                ['source', [1, 1, 1, 3]], +                [ +                    ['combine', 3, 2] +                ] +            ], +            [ +                ['source', null], +                ['source', [3, 3]], +                [ +                    ['combine', 0, 2], +                    ['combine', 1, 2] +                ] +            ], +            [ +                ['source', null], +                ['source', [3, 3]], +                [ +                    ['combine', 3, 2], +                    ['combine', 0, 2] +                ] +            ], + +            // Insert +            [ +                ['source', null], +                ['source', [0, 1, 1, 1, 1, 1, 1]], +                [ +                    ['insert', 0, 0] +                ] +            ], +            [ +                ['source', null], +                ['source', [1, 1, 1, 1, 1, 1, 0]], +                [ +                    ['insert', 6, 0] +                ] +            ], +            [ +                ['source', null], +                ['source', [0, 1, 1, 1, 1, 1, 1, 0]], +                [ +                    ['insert', 0, 0], +                    ['insert', 7, 0] +                ] +            ], +            [ +                ['source', null], +                ['source', [0, 1, 1, 1, 1, 1, 1, 0]], +                [ +                    ['insert', 6, 0], +                    ['insert', 0, 0] +                ] +            ], + +            // Mixed +            [ +                ['source', null], +                ['source', [3, 0, 3]], +                [ +                    ['combine', 0, 2], +                    ['insert', 1, 0], +                    ['combine', 2, 2] +                ] +            ], +            [ +                ['source', null], +                ['source', [3, 0, 3]], +                [ +                    ['combine', 0, 2], +                    ['combine', 1, 2], +                    ['insert', 1, 0] +                ] +            ], +            [ +                ['source', null], +                ['source', [3, 0, 3]], +                [ +                    ['insert', 3, 0], +                    ['combine', 0, 2], +                    ['combine', 2, 2] +                ] +            ] +        ]; + +        for (const [[source, mapping], [expectedSource, expectedMapping], operations] of data) { +            const sourceMap = new TextSourceMap(source, mapping); +            const expectedSourceMap = new TextSourceMap(expectedSource, expectedMapping); +            for (const [operation, ...args] of operations) { +                switch (operation) { +                    case 'combine': +                        sourceMap.combine(...args); +                        break; +                    case 'insert': +                        sourceMap.insert(...args); +                        break; +                } +            } +            expect(sourceMap.equals(expectedSourceMap)).toBe(true); +        } +    }); +} + + +function main() { +    testSource(); +    testEquals(); +    testGetSourceLength(); +    testCombineInsert(); +} + + +main(); diff --git a/test/translator.test.js b/test/translator.test.js new file mode 100644 index 00000000..7a827d39 --- /dev/null +++ b/test/translator.test.js @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {IDBKeyRange, indexedDB} from 'fake-indexeddb'; +import fs from 'fs'; +import {fileURLToPath} from 'node:url'; +import path from 'path'; +import {expect, test, vi} from 'vitest'; +import {TranslatorVM} from '../dev/translator-vm'; + +vi.stubGlobal('indexedDB', indexedDB); +vi.stubGlobal('IDBKeyRange', IDBKeyRange); + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function main() { +    const translatorVM = new TranslatorVM(); +    const dictionaryDirectory = path.join(dirname, 'data', 'dictionaries', 'valid-dictionary1'); +    await translatorVM.prepare(dictionaryDirectory, 'Test Dictionary 2'); + +    const testInputsFilePath = path.join(dirname, 'data', 'translator-test-inputs.json'); +    const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'})); + +    const testResults1FilePath = path.join(dirname, 'data', 'translator-test-results.json'); +    const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'})); +    const actualResults1 = []; + +    const testResults2FilePath = path.join(dirname, 'data', 'translator-test-results-note-data1.json'); +    const expectedResults2 = JSON.parse(fs.readFileSync(testResults2FilePath, {encoding: 'utf8'})); +    const actualResults2 = []; + +    for (let i = 0, ii = tests.length; i < ii; ++i) { +        test(`${i}`, async () => { +            const t = tests[i]; +            const expected1 = expectedResults1[i]; +            const expected2 = expectedResults2[i]; +            switch (t.func) { +                case 'findTerms': +                    { +                        const {name, mode, text} = t; +                        const options = translatorVM.buildOptions(optionsPresets, t.options); +                        const {dictionaryEntries, originalTextLength} = structuredClone(await translatorVM.translator.findTerms(mode, text, options)); +                        const noteDataList = mode !== 'simple' ? structuredClone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(structuredClone(dictionaryEntry), mode))) : null; +                        actualResults1.push({name, originalTextLength, dictionaryEntries}); +                        actualResults2.push({name, noteDataList}); +                        expect(originalTextLength).toStrictEqual(expected1.originalTextLength); +                        expect(dictionaryEntries).toStrictEqual(expected1.dictionaryEntries); +                        expect(noteDataList).toEqual(expected2.noteDataList); +                    } +                    break; +                case 'findKanji': +                    { +                        const {name, text} = t; +                        const options = translatorVM.buildOptions(optionsPresets, t.options); +                        const dictionaryEntries = structuredClone(await translatorVM.translator.findKanji(text, options)); +                        const noteDataList = structuredClone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(structuredClone(dictionaryEntry), null))); +                        actualResults1.push({name, dictionaryEntries}); +                        actualResults2.push({name, noteDataList}); +                        expect(dictionaryEntries).toStrictEqual(expected1.dictionaryEntries); +                        expect(noteDataList).toEqual(expected2.noteDataList); +                    } +                    break; +            } +        }); +    } +} + +await main(); diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 00000000..9e1c54a5 --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023  Yomitan 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/>. + */ +import {configDefaults, defineConfig} from 'vitest/config'; + +export default defineConfig({ +    test: { +        exclude: [ +            ...configDefaults.exclude, +            'dev/lib/**', +            'test/playwright/**' +        ], +        environment: 'jsdom', +        poolOptions: { +            threads: { +                useAtomics: true +            } +        } +    } +});  |