diff --git a/README.md b/README.md index 974e07a2..03b10951 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,36 @@ # [sudoku](https://sudoku.jonasgeiler.com) -This is a very simple sudoku game built with Svelte and TailwindCSS. -Have fun! 😉 +# 数独乐乐 OOAD + +逆向分析现有数独项目的面向对象技术,然后按新业务愿景,改进升级为数独乐乐应用;分析、设计出面向对象技术方案,并落地代码实现。现有数独项目:https://github.com/jonasgeiler/sudoku + + + +# 逆向分析 + +逆向工程,对现有项目的OOA、OOD、OOP进行分析: + +1. 讨论其设计思想、设计原则和使用的设计模式,给出其**愿景**、**用例分析**、**领域模型**、**技术架构**与**对象模型**; +2. 结合课程,评价现有OOD架构与设计的优劣,给出**改进建议**。新业务愿景升级应用为**数独乐乐**。让游戏更加适合初学者,给用户更多友好指引。同时,集成SudokuWiki.org,便于持续提升求解算法和验证开发。下一步提示通过提示按钮,为用户提示下一步可以选择的答案,并提示线索,说明推理答案所用的方法(策略)。 + + + + + +# 团队合作任务 + +- [x] 下一步提示:过提示按钮,为用户提示下一步可以选择的答案,并提示线索,说明推理答案所用的方法(策略)。 + 下一步提示的主要功能已经实现,具体说明在 下一步提示说明文档 文件夹中。 + +- [x] 探索回溯:方便用户在多种可能得答案中,漫游、探索、回溯 + +资源集成:集成数独Wiki资源,包括题目、算法策略。 https://www.sudokuwiki.org/Sudoku.htm + +1. - [x] 题目导入:能够将数独Wiki的题目页的URL作为Code,直接导入到数独乐乐中使用。 + +2. - [x] 算法策略:给出良好的OOAD,能够高效、正确地在数独乐乐中引入、集成数独Wiki的各种算法策略。重点在于, + + 1. 如何验证策略算法正确性; + 2. 如何在确保算法独立性,即新算法的引入不会影响老算法的正确性,也不会降低老算法的与性能。 -> [!WARNING] -> Unfortunately not all features are done yet. Specifically: -> - Undoing/redoing moves -> - Creating your own sudoku games \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..700649b0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4261 @@ +{ + "name": "sudoku", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sudoku", + "version": "1.0.0", + "dependencies": { + "@mattflow/sudoku-solver": "2.2.0", + "fake-sudoku-puzzle-generator": "1.2.1", + "postcss-load-config": "^2.1.2", + "sirv-cli": "1.0.0", + "tailwindcss": "1.9.6" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "16.0.0", + "@rollup/plugin-node-resolve": "10.0.0", + "autoprefixer": "10.0.2", + "inline-critical": "6.0.1", + "postcss": "8.4.31", + "postcss-clean": "1.1.0", + "postcss-import": "13.0.0", + "reaver": "2.0.0", + "rimraf": "3.0.2", + "rollup": "2.33.3", + "rollup-plugin-copy": "3.3.0", + "rollup-plugin-css-only": "3.0.0", + "rollup-plugin-livereload": "2.0.0", + "rollup-plugin-svelte": "7.0.0", + "rollup-plugin-terser": "7.0.2", + "svelte": "3.49.0", + "svelte-preprocess": "4.6.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", + "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", + "license": "MIT", + "dependencies": { + "postcss": "7.0.32", + "purgecss": "^2.3.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mattflow/sudoku-solver": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/@mattflow/sudoku-solver/-/sudoku-solver-2.2.0.tgz", + "integrity": "sha512-qbkFBFSVKti6TWIr+vWFimhaIKTIuK7wDJv0CB61WchU2BC7ZGIfYid7ZTgpPnHL4HVMwCHZsFd6PeGKQCqwjA==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "16.0.0", + "resolved": "https://registry.npmmirror.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz", + "integrity": "sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^2.30.0" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "10.0.0", + "resolved": "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz", + "integrity": "sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmmirror.com/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sass": { + "version": "1.43.1", + "resolved": "https://registry.npmmirror.com/@types/sass/-/sass-1.43.1.tgz", + "integrity": "sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "license": "Apache-2.0", + "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.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.0.2", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.0.2.tgz", + "integrity": "sha512-okBmu9OMdt6DNEcZmnl0IYVv8Xl/xYWRSnc2OJ9UJEOt1u30opG1B8aLsViqKryBaYv1SKB4f85fOGZs5zYxHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.14.7", + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001687", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", + "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" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/console-clear": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/console-clear/-/console-clear-1.1.1.tgz", + "integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "license": "MIT", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.71", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", + "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", + "license": "ISC" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fake-sudoku-puzzle-generator": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/fake-sudoku-puzzle-generator/-/fake-sudoku-puzzle-generator-1.2.1.tgz", + "integrity": "sha512-jqVw6MKjmsX/A2l1f/UvH1bNXiHx4tQrprpMZxeN1hjoTwUO76BzbdJH8srHqYIEynGOWqTRCMBKUi6JxlcxoQ==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fg-loadcss": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fg-loadcss/-/fg-loadcss-2.1.0.tgz", + "integrity": "sha512-HpvR2uRoKvrYAEwimw+k4Fr2NVHYPfld5Lc/f9uy3mKeUTXhS5urL24XA2rqyq5b2i410EXCLir4Uhsb8J1QaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-3.0.2.tgz", + "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "license": "MIT", + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "license": "MIT", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inline-critical": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/inline-critical/-/inline-critical-6.0.1.tgz", + "integrity": "sha512-utCG2x6XiNKKqTZs3DJwFCOCy3sXyu3grVXVfJmgfyceDhPp1p5QeE1uAXjIzWDvcosMxD/h4aIa/sZWgWPsSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "clean-css": "^4.2.3", + "css": "^3.0.0", + "detect-indent": "^6.0.0", + "fg-loadcss": "^2.1.0", + "get-stdin": "^8.0.0", + "indent-string": "^4.0.0", + "jsdom": "^16.4.0", + "lodash.defaults": "^4.2.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isregexp": "^4.0.1", + "lodash.isstring": "^4.0.1", + "meow": "^7.1.1", + "normalize-newline": "^3.0.0", + "postcss": "^7.0.32", + "postcss-discard": "^1.0.0", + "prettier": "^2.1.1", + "reaver": "^2.0.0", + "slash": "^3.0.0", + "uglify-js": "^3.10.4" + }, + "bin": { + "inline-critical": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/inline-critical/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inline-critical/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmmirror.com/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-access": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/local-access/-/local-access-1.1.0.tgz", + "integrity": "sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isregexp": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/lodash.isregexp/-/lodash.isregexp-4.0.1.tgz", + "integrity": "sha512-rw9+95tYcUa9nQ1FgdtKvO+hReLGNqnNMHfLq8SwK5Mo6D0R0tIsnRHGHaTHSKeYBaLCJ1JvXWdz4UmpPZ2bag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmmirror.com/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" + }, + "node_modules/normalize-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-newline/-/normalize-newline-3.0.0.tgz", + "integrity": "sha512-uLZbfjzZfHTlaGXMJkwc5TUEhY7/+LHvP1X/OcDt6SLkubrshIOg7hbT6rkmAhyvGpi6kJ+XcMfwM7D3/Zieqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==", + "license": "MIT" + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "license": "MIT" + }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-clean": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/postcss-clean/-/postcss-clean-1.1.0.tgz", + "integrity": "sha512-83g3GqMbCM5NL6MlbbPLJ/m2NrUepBF44MoDk4Gt04QGXeXKh9+ilQa0DzLnYnvqYHQCw83nckuEzBFr2muwbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-css": "^4.x", + "postcss": "^6.x" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-clean/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-clean/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-clean/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-clean/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-clean/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-clean/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-clean/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/postcss-discard/-/postcss-discard-1.0.3.tgz", + "integrity": "sha512-GSEw59lvAr29wvsDQNbuI5hQHeXto+8UaM7VsNzRp0vJyfhNDc9xNDzxtKml4hO/kf7QxcB5dlaZj8lyBnthtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-css": "^5.1.2", + "lodash.isfunction": "^3.0.9", + "lodash.isregexp": "^4.0.1", + "postcss": "^8.3.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-discard/node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha512-N5yWXWKA+uhpLQ9ZhBRl2bIAdM6oVJYpDojuI1nF2SzXBimJcdjFwiAouBVbO5VuOF3qA6BSFWFc3wXbbj72XQ==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-functions/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/postcss-functions/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "license": "MIT" + }, + "node_modules/postcss-functions/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-import": { + "version": "13.0.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-13.0.0.tgz", + "integrity": "sha512-LPUbm3ytpYopwQQjqgUH4S3EM/Gb9QsaSPP/5vnoi+oKVy3/mIk2sc0Paqw7RL57GpScm9MdIMUypw2znWiBpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, + "node_modules/postcss-js/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "license": "ISC" + }, + "node_modules/postcss-js/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-nested": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-4.2.3.tgz", + "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", + "license": "MIT", + "dependencies": { + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-nested/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "license": "ISC" + }, + "node_modules/postcss-nested/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/purgecss/-/purgecss-2.3.0.tgz", + "integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==", + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.0.0", + "postcss": "7.0.32", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss" + } + }, + "node_modules/purgecss/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/purgecss/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/purgecss/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/purgecss/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/purgecss/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/purgecss/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/purgecss/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/purgecss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reaver": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/reaver/-/reaver-2.0.0.tgz", + "integrity": "sha512-VqtmjtpRh3XwBQTLM/KbgV2ERBr4jXwY+8hkcNXNCeGb2uY4KQNigAbHDTLGrDm9tIYiczG4ADI5/1z4teRRjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.1.0" + }, + "bin": { + "reaver": "bin/reaver" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "license": "MIT", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "license": "MIT" + }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmmirror.com/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", + "dev": true, + "license": "MIT" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.33.3", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-2.33.3.tgz", + "integrity": "sha512-RpayhPTe4Gu/uFGCmk7Gp5Z9Qic2VsqZ040G+KZZvsZYdcuWaJg678JeDJJvJeEQXminu24a2au+y92CUWVd+w==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" + } + }, + "node_modules/rollup-plugin-copy": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz", + "integrity": "sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-css-only": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/rollup-plugin-css-only/-/rollup-plugin-css-only-3.0.0.tgz", + "integrity": "sha512-v6ZZuApxXl/Z07jAmH+p5HsLPMW2BHIRJ4Tpxv22S+jpINZ1QFxWoeMDMyFDd+XM1jFcQeshkjuD8xUVOJJG7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "3 || 4" + }, + "engines": { + "node": ">=10.12.0" + }, + "peerDependencies": { + "rollup": "1 || 2" + } + }, + "node_modules/rollup-plugin-livereload": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz", + "integrity": "sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "livereload": "^0.9.1" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-svelte": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/rollup-plugin-svelte/-/rollup-plugin-svelte-7.0.0.tgz", + "integrity": "sha512-cw4yv/5v1NQV3nPbpOJtikgkB+9mfSJaqKUdq7x5fVQJnwLtcdc2JOszBs5pBY+SemTs5pmJbdEMseEavbUtjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "rollup": ">=2.0.0", + "svelte": ">=3.5.0" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmmirror.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/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" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semiver": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/semiver/-/semiver-1.1.0.tgz", + "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sirv-cli": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/sirv-cli/-/sirv-cli-1.0.0.tgz", + "integrity": "sha512-84PvgoXKbUmyO3i4kHFSrXuerD2iGIcewBgdmghwjeK1nRACJ8gG/vOH1qZdBDJMZGBC8miHs7erNFwv0mXE1A==", + "license": "MIT", + "dependencies": { + "console-clear": "^1.1.0", + "get-port": "^3.2.0", + "kleur": "^3.0.0", + "local-access": "^1.0.1", + "sade": "^1.6.0", + "semiver": "^1.0.0", + "sirv": "^1.0.0", + "tinydate": "^1.0.0" + }, + "bin": { + "sirv": "bin.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "3.49.0", + "resolved": "https://registry.npmmirror.com/svelte/-/svelte-3.49.0.tgz", + "integrity": "sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/svelte-preprocess": { + "version": "4.6.1", + "resolved": "https://registry.npmmirror.com/svelte-preprocess/-/svelte-preprocess-4.6.1.tgz", + "integrity": "sha512-s7KdhR2pOsffyOzZIMEb315f6pfgeDnOWN47m6YKFeSEx3NMf/79Znc3vuG/Ai79SL/vsi86WDrjFPLGRfDesg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@types/pug": "^2.0.4", + "@types/sass": "^1.16.0", + "detect-indent": "^6.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">= 9.11.2" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3", + "postcss": "^7 || ^8", + "postcss-load-config": "^2.1.0", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": "^0.54.7", + "sugarss": "^2.0.0", + "svelte": "^3.23.0", + "typescript": "^3.9.5 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "1.9.6", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-1.9.6.tgz", + "integrity": "sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ==", + "license": "MIT", + "dependencies": { + "@fullhuman/postcss-purgecss": "^2.1.2", + "autoprefixer": "^9.4.5", + "browserslist": "^4.12.0", + "bytes": "^3.0.0", + "chalk": "^3.0.0 || ^4.0.0", + "color": "^3.1.2", + "detective": "^5.2.0", + "fs-extra": "^8.0.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.20", + "node-emoji": "^1.8.1", + "normalize.css": "^8.0.1", + "object-hash": "^2.0.3", + "postcss": "^7.0.11", + "postcss-functions": "^3.0.0", + "postcss-js": "^2.0.0", + "postcss-nested": "^4.1.1", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "reduce-css-calc": "^2.1.6", + "resolve": "^1.14.2" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/tailwindcss/node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/tailwindcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "license": "ISC" + }, + "node_modules/tailwindcss/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/terser": { + "version": "5.37.0", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinydate": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/tinydate/-/tinydate-1.3.0.tgz", + "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json index 505e46f3..31cb6612 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "postcss": "8.4.31", "postcss-clean": "1.1.0", "postcss-import": "13.0.0", - "postcss-load-config": "3.0.0", "reaver": "2.0.0", "rimraf": "3.0.2", "rollup": "2.33.3", @@ -32,6 +31,7 @@ "dependencies": { "@mattflow/sudoku-solver": "2.2.0", "fake-sudoku-puzzle-generator": "1.2.1", + "postcss-load-config": "^2.1.2", "sirv-cli": "1.0.0", "tailwindcss": "1.9.6" } diff --git a/src/App.svelte b/src/App.svelte index fe431c69..49a45431 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -4,6 +4,7 @@ import game from '@sudoku/game'; import { modal } from '@sudoku/stores/modal'; import { gameWon } from '@sudoku/stores/game'; + import { hintstate } from '@sudoku/stores/hints'; import Board from './components/Board/index.svelte'; import Controls from './components/Controls/index.svelte'; import Header from './components/Header/index.svelte'; @@ -16,6 +17,13 @@ } }); + hintstate.subscribe(state => { + console.log("---------------更新---------------------",state); + if (!state) { + modal.show('nosolution'); + } + }); + onMount(() => { let hash = location.hash; diff --git a/src/components/Board/Cell.svelte b/src/components/Board/Cell.svelte index b71b3603..e989162a 100644 --- a/src/components/Board/Cell.svelte +++ b/src/components/Board/Cell.svelte @@ -3,6 +3,12 @@ import { fade } from 'svelte/transition'; import { SUDOKU_SIZE } from '@sudoku/constants'; import { cursor } from '@sudoku/stores/cursor'; + import { hintStore } from '@sudoku/stores/hintStore'; // 导入 hintStore + import { userGrid } from '@sudoku/stores/grid'; + import { sudokutreemanager } from "@sudoku/stores/state"; + import { hintText } from '@sudoku/stores/hints'; // 导入 hintText + import { reasons_list } from '@sudoku/stores/hints'; // 导入 reasons_list + import { get } from 'svelte/store'; //导入 svelte 的 get 方法 export let value; export let cellX; @@ -20,33 +26,87 @@ const borderRightBold = (cellX !== SUDOKU_SIZE && cellX % 3 === 0); const borderBottom = (cellY !== SUDOKU_SIZE && cellY % 3 !== 0); const borderBottomBold = (cellY !== SUDOKU_SIZE && cellY % 3 === 0); + + // 用于存储所有提示内容 + let allHints = []; + + // 订阅 hintStore,获取所有提示数据 + hintStore.subscribe(hints => { + allHints = hints; // 保存整个 hintStore 列表 + }); + + function handleCellClick() { + cursor.set(cellX - 1, cellY - 1); + // 查找是否有提示与当前单元格匹配 + const matchingHint = allHints.find(hint => hint.x === cellX - 1 && hint.y === cellY - 1); + let hintval = 0; + if (matchingHint) { + // 如果找到匹配的提示,将提示的值填入 userGrid 对应位置 + if (matchingHint.value.length === 1) { + hintval = matchingHint.value[0]; + userGrid.set({ x: cellX - 1, y: cellY - 1 }, hintval); + } + else { + sudokutreemanager.pushToStack(); + } + + // 如果找到匹配的提示,才设置显示提示的原因 + let reasons = get(reasons_list); // 提取 writable 的当前值 + console.log("reasons:",reasons); + const matchingReason = reasons[cellY-1][cellX-1]; + console.log("cellX-1:",cellX-1,"cellY-1:",cellY-1,"matchingReason:",matchingReason); + if (matchingReason && matchingReason.length) { + hintText.set(matchingReason); + console.log(matchingReason[0]); + } + + } else { + hintText.set(""); + } + + // 清空 hintStore 的内容 + hintStore.clear(); + } +
+ class:border-r={borderRight} + class:border-r-4={borderRightBold} + class:border-b={borderBottom} + class:border-b-4={borderBottomBold}> {#if !disabled}
+ class:user-number={userNumber} + class:selected={selected} + class:same-area={sameArea} + class:same-number={sameNumber} + class:conflicting-number={conflictingNumber}> - +
{/if} -
\ No newline at end of file + + diff --git a/src/components/Board/index.svelte b/src/components/Board/index.svelte index 6fcf65f5..27be7837 100644 --- a/src/components/Board/index.svelte +++ b/src/components/Board/index.svelte @@ -6,6 +6,7 @@ import { cursor } from '@sudoku/stores/cursor'; import { candidates } from '@sudoku/stores/candidates'; import Cell from './Cell.svelte'; + import { hintText } from '@sudoku/stores/hints'; // 导入提示文字状态 function isSelected(cursorStore, x, y) { return cursorStore.x === x && cursorStore.y === y; @@ -30,6 +31,12 @@
+ + +
+ {$hintText} +
+
@@ -61,4 +68,16 @@ .board-padding { @apply px-4 pb-4; } + + .hint-text { + position: absolute; + top: 0; + left: 0; + padding: 8px; + /*background-color: rgba(255, 255, 255, 0.75); !* 半透明背景 *!*/ + /*border-radius: 0 0 8px 0; !* 右下角圆角 *!*/ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影 */ + z-index: 20; /* 确保提示文字在棋盘之上 */ + } + \ No newline at end of file diff --git a/src/components/Controls/ActionBar/Actions.svelte b/src/components/Controls/ActionBar/Actions.svelte index 2fdaee54..f4cbf25d 100644 --- a/src/components/Controls/ActionBar/Actions.svelte +++ b/src/components/Controls/ActionBar/Actions.svelte @@ -1,61 +1,175 @@
+ - - -
- \ No newline at end of file + diff --git a/src/components/Header/index.svelte b/src/components/Header/index.svelte index 584bcd60..e5403fa3 100644 --- a/src/components/Header/index.svelte +++ b/src/components/Header/index.svelte @@ -1,3 +1,5 @@ + + + +

No Solotion

+ + + +
+ + +
\ No newline at end of file diff --git a/src/components/Modal/Types/Settings.svelte b/src/components/Modal/Types/Settings.svelte index f21ffeff..31e649fa 100644 --- a/src/components/Modal/Types/Settings.svelte +++ b/src/components/Modal/Types/Settings.svelte @@ -36,6 +36,17 @@
+ +
+ + +
+ + diff --git a/src/components/Modal/Types/Welcome.svelte b/src/components/Modal/Types/Welcome.svelte index 5076e372..34775125 100644 --- a/src/components/Modal/Types/Welcome.svelte +++ b/src/components/Modal/Types/Welcome.svelte @@ -11,6 +11,7 @@ let sencode = data.sencode || ''; $: enteredSencode = sencode.trim().length !== 0; + // $: buttonDisabled = enteredSencode ? !validateSencode(sencode) : !DIFFICULTIES.hasOwnProperty(difficulty); function handleStart() { diff --git a/src/components/Modal/Types/index.js b/src/components/Modal/Types/index.js index 54b8810a..3eb94fad 100644 --- a/src/components/Modal/Types/index.js +++ b/src/components/Modal/Types/index.js @@ -5,6 +5,7 @@ import confirm from './Confirm.svelte'; import prompt from './Prompt.svelte'; import welcome from './Welcome.svelte'; import gameover from './GameOver.svelte'; +import nosolution from './Nosolution.svelte'; export default { share, @@ -13,5 +14,6 @@ export default { confirm, prompt, welcome, - gameover + gameover, + nosolution } \ No newline at end of file diff --git a/src/main.js b/src/main.js index 3b5b5314..ac240469 100644 --- a/src/main.js +++ b/src/main.js @@ -7,6 +7,9 @@ const app = new App({ export default app; +// Ŀļ +// App.svelte ص index.html еĸԪأ
+ // TODO: Warn when hint not possible // TODO: Undo/Redo // TODO: Import sudoku diff --git a/src/node_modules/@sudoku/constants.js b/src/node_modules/@sudoku/constants.js index 9f9ae36c..f390f3c5 100644 --- a/src/node_modules/@sudoku/constants.js +++ b/src/node_modules/@sudoku/constants.js @@ -12,10 +12,11 @@ export const DEFAULT_SETTINGS = { darkTheme: false, displayTimer: true, hintsLimited: true, - hints: 5, + hints: 100, highlightCells: true, highlightSame: true, highlightConflicting: true, + hintLevel: 1, // 新增 hintLevel,默认值为 1 }; export const MAX_HINTS = 99999; diff --git a/src/node_modules/@sudoku/game.js b/src/node_modules/@sudoku/game.js index 16d1226e..83600823 100644 --- a/src/node_modules/@sudoku/game.js +++ b/src/node_modules/@sudoku/game.js @@ -1,10 +1,10 @@ import { cursor } from './stores/cursor'; import { difficulty } from './stores/difficulty'; import { gamePaused } from './stores/game'; -import { grid } from './stores/grid'; +import { grid, userGrid } from './stores/grid'; import { timer } from './stores/timer'; import { hints } from './stores/hints'; - +import { sudokutreemanager } from '@sudoku/stores/state'; /** * Start new game with a generated sudoku * @@ -16,8 +16,9 @@ export function startNew(diff) { cursor.reset(); timer.reset(); hints.reset(); - + sudokutreemanager.generate(userGrid.getUserGridCopy()); location.hash = ''; + } /** diff --git a/src/node_modules/@sudoku/sencode/index.js b/src/node_modules/@sudoku/sencode/index.js index d12c770c..f34a783b 100644 --- a/src/node_modules/@sudoku/sencode/index.js +++ b/src/node_modules/@sudoku/sencode/index.js @@ -53,8 +53,26 @@ export function encodeSudoku(sudoku) { /** * @param {string} sencode * @returns {number[][]} + * ֱӰַʽȻ˳ضӦһЩͺˡ + * һ */ +function validateLength(str){ + + return str.length==81 +} + +function isNumeric(str) { + return /^[0-9]+$/.test(str); +} + +function isNumDot(str) { + return /^[0-9.]+$/.test(str); + +} export function decodeSencode(sencode) { + if(sencode.includes("bd=") != -1){ + sencode = sencode.split("bd=")[1] + } let grid = [ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -67,38 +85,73 @@ export function decodeSencode(sencode) { [0, 0, 0, 0, 0, 0, 0, 0, 0], ]; - const reversed = sencode.indexOf(SEPARATOR_REVERSE) !== -1; - - const [encodedStructure, encodedNumbers] = sencode.split(reversed ? SEPARATOR_REVERSE : SEPARATOR); + if(validateLength(sencode) && isNumeric(sencode)) { + let place =0 + for(let row =0;row<9;row++){ + for(let col=0;col<9;col++,place++){ + grid[row][col] = sencode[place] - '0'; - const structure = Base62.decode(encodedStructure) - .toString(2) - .padStart(GRID_LENGTH, '0'); + } - let numberCount = 0; - for (let cell = 0; cell < GRID_LENGTH; cell++) { - if (structure[cell] === '1') { - numberCount++; } + + } + else if(validateLength(sencode) && isNumDot(sencode)){ + let place =0 + for(let row =0;row<9;row++){ + for(let col=0;col<9;col++,place++){ + if(sencode[place] == '.') { + grid[row][col] = 0; + continue; + } - let numbers = Base62.decode(encodedNumbers) - .toString() - .padStart(numberCount, '0') - .split(''); + grid[row][col] = sencode[place]- '0'; + + } - for (let cell = 0; cell < GRID_LENGTH; cell++) { - if (structure[cell] === '1') { - const [row, col] = GRID_COORDS[cell]; - grid[row][col] = numbers.shift() * 1 + 1; } + + } + else{ + const reversed = sencode.indexOf(SEPARATOR_REVERSE) !== -1; + + const [encodedStructure, encodedNumbers] = sencode.split(reversed ? SEPARATOR_REVERSE : SEPARATOR); + + const structure = Base62.decode(encodedStructure) + .toString(2) + .padStart(GRID_LENGTH, '0'); + + let numberCount = 0; + for (let cell = 0; cell < GRID_LENGTH; cell++) { + if (structure[cell] === '1') { + numberCount++; + } + } + + let numbers = Base62.decode(encodedNumbers) + .toString() + .padStart(numberCount, '0') + .split(''); + + for (let cell = 0; cell < GRID_LENGTH; cell++) { + if (structure[cell] === '1') { + const [row, col] = GRID_COORDS[cell]; + grid[row][col] = numbers.shift() * 1 + 1; + } + } + + if (reversed) { + // Reverse Grid + grid = grid.reverse().map(row => row.reverse()); + } - if (reversed) { - // Reverse Grid - grid = grid.reverse().map(row => row.reverse()); } + + + return grid; } @@ -108,5 +161,20 @@ export function decodeSencode(sencode) { * @returns {boolean} */ export function validateSencode(sencode) { + + + + // ַʹܹ֮ʽurl + // https://www.sudokuwiki.org/sudoku.htm?bd=291.7...3...3....7.....6..83.....42....4.7....42......4..5.....8....1.......8.975 + var p = sencode.slice() + if(p.includes("bd=")){ + p = p.split("bd=")[1] + } + + // type 1 + if(validateLength(p) && isNumeric(p)) return true + // type 2 + if(validateLength(p) && isNumDot(p) ) return true + // type 3 return sencode && sencode.trim().length !== 0 && SENCODE_REGEX.test(sencode); } \ No newline at end of file diff --git a/src/node_modules/@sudoku/stores/grid.js b/src/node_modules/@sudoku/stores/grid.js index f803807b..fbc35d11 100644 --- a/src/node_modules/@sudoku/stores/grid.js +++ b/src/node_modules/@sudoku/stores/grid.js @@ -1,106 +1,137 @@ import { BOX_SIZE, SUDOKU_SIZE } from '@sudoku/constants'; import { decodeSencode, encodeSudoku } from '@sudoku/sencode'; -import { generateSudoku, solveSudoku } from '@sudoku/sudoku'; +import { generateSudoku } from '@sudoku/sudoku'; import { derived, writable } from 'svelte/store'; +import { hintStore } from '@sudoku/stores/hintStore'; import { hints } from './hints'; +import { get } from 'svelte/store'; +import { sudokutreemanager } from '@sudoku/stores/state'; -function createGrid() { - const grid = writable([ - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - ]); - - return { - subscribe: grid.subscribe, - - generate(difficulty) { - grid.set(generateSudoku(difficulty)); - }, - - decodeSencode(sencode) { - grid.set(decodeSencode(sencode)); - }, - - get(gridStore, x, y) { - return gridStore[y][x]; - }, - - getSencode(gridStore) { - return encodeSudoku(gridStore); - }, - }; + +class Grid { + #grid; + + constructor() { + this.#grid = writable( + Array(SUDOKU_SIZE).fill().map(() => Array(SUDOKU_SIZE).fill(0)) + ); + } + + subscribe(callback) { + return this.#grid.subscribe(callback); + } + + generate(difficulty) { + this.#grid.set(generateSudoku(difficulty)); + } + + decodeSencode(sencode) { + this.#grid.set(decodeSencode(sencode)); + } + + get(gridStore, x, y) { + return gridStore[y][x]; + } + + getSencode(gridStore) { + return encodeSudoku(gridStore); + } + + getGridCopy() { + const currentValue = get(this.#grid); + // 深拷贝二维数组 + const copy = currentValue.map(row => [...row]); + return copy; + } } -export const grid = createGrid(); - - -function createUserGrid() { - const userGrid = writable([ - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0], - ]); - - grid.subscribe($grid => { - let newGrid = []; - - for (let y = 0; y < SUDOKU_SIZE; y++) { - newGrid[y] = []; - for (let x = 0; x < SUDOKU_SIZE; x++) { - newGrid[y][x] = $grid[y][x]; - } +export const grid = new Grid(); + +class UserGrid { + #userGrid; + + constructor(gridInstance) { + this.#userGrid = writable( + Array(SUDOKU_SIZE).fill().map(() => Array(SUDOKU_SIZE).fill(0)) + ); + + gridInstance.subscribe($grid => { + const newGrid = $grid.map(row => [...row]); + this.#userGrid.set(newGrid); + }); + } + + subscribe(callback) { + return this.#userGrid.subscribe(callback); + } + + set(pos, value) { + this.#userGrid.update($userGrid => { + console.log("The current node before:", sudokutreemanager.currentNode.grid); + $userGrid[pos.y][pos.x] = value; + + const CopiedGrid = this.getUserGridCopy(); + console.log("The current node:", sudokutreemanager.currentNode.grid); + sudokutreemanager.createChildNode(CopiedGrid); + return $userGrid; + }); + } + + /** + * 与 hints.js 交互,获取提示值并保存到 hintStore 中。 + * @param {Object} pos 提示的坐标 { x, y } + */ + applyHint(pos) { + + const currentUserGrid = get(this.#userGrid); + + let solve = hints.useHint(currentUserGrid); + console.log("Solve:", solve); + + if (solve instanceof Array) { + hintStore.hint_array_set(solve); } - userGrid.set(newGrid); - }); - - return { - subscribe: userGrid.subscribe, - - set: (pos, value) => { - userGrid.update($userGrid => { - $userGrid[pos.y][pos.x] = value; - return $userGrid; - }); - }, - - applyHint: (pos) => { - hints.useHint(); - userGrid.update($userGrid => { - const solvedSudoku = solveSudoku($userGrid); - $userGrid[pos.y][pos.x] = solvedSudoku[pos.y][pos.x]; - return $userGrid; - }); - }, - }; + } + getUserGridCopy() { + // 先取出当前的值 + const currentGrid = get(this.#userGrid); + + // 利用 map + 扩展运算符(或 row.slice())进行二维数组的深拷贝 + const deepCopiedGrid = currentGrid.map(row => [...row]); + + return deepCopiedGrid; + } + + setGrid(newGrid) { + + if ( + !Array.isArray(newGrid) || + newGrid.length !== SUDOKU_SIZE || + !newGrid.every(row => Array.isArray(row) && row.length === SUDOKU_SIZE) + ) { + throw new Error('Invalid grid format'); + } + + // 设置新的网格 + this.#userGrid.set(newGrid); + // 打印当前的 #grid + console.log(this.#userGrid); + } } -export const userGrid = createUserGrid(); +export const userGrid = new UserGrid(grid); export const invalidCells = derived(userGrid, $userGrid => { const _invalidCells = []; const addInvalid = (x, y) => { - const xy = x + ',' + y; + const xy = `${x},${y}`; if (!_invalidCells.includes(xy)) _invalidCells.push(xy); }; for (let y = 0; y < SUDOKU_SIZE; y++) { for (let x = 0; x < SUDOKU_SIZE; x++) { - const value = $userGrid[y][x]; if (value) { @@ -129,9 +160,8 @@ export const invalidCells = derived(userGrid, $userGrid => { } } } - } } return _invalidCells; -}, []); \ No newline at end of file +}, []); diff --git a/src/node_modules/@sudoku/stores/hintStore.js b/src/node_modules/@sudoku/stores/hintStore.js new file mode 100644 index 00000000..31296f87 --- /dev/null +++ b/src/node_modules/@sudoku/stores/hintStore.js @@ -0,0 +1,39 @@ +import { writable } from 'svelte/store'; + +class HintStore { + constructor() { + // 初始化一个可写的 Svelte store,用于存储提示列表 + this.hints = writable([]); + } + + // 订阅 store 的方法,用于在组件中获取提示数据 + subscribe(run, invalidate) { + return this.hints.subscribe(run, invalidate); + } + + // 添加一个提示到提示列表中 + add(pos, [value]) { + this.hints.update(current => [...current, { x: pos.x, y: pos.y, value:[value] }]); + } + + // 将一个列表直接设置为 hints + hint_array_set(list) { + if (list instanceof Array) { + this.hints.set(list); + } + } + + // 根据索引移除指定的提示 + remove(index) { + this.hints.update(current => current.filter((_, i) => i !== index)); + } + + // 清空所有提示 + clear() { + this.hints.set([]); + } +} + +// 导出 HintStore 实例 +export const hintStore = new HintStore(); + diff --git a/src/node_modules/@sudoku/stores/hints.js b/src/node_modules/@sudoku/stores/hints.js index 4b09e5a4..748fb491 100644 --- a/src/node_modules/@sudoku/stores/hints.js +++ b/src/node_modules/@sudoku/stores/hints.js @@ -1,46 +1,127 @@ -import { settings } from './settings'; import { writable } from 'svelte/store'; +import { settings } from './settings'; +import { solveSudoku } from '@sudoku/sudoku'; +import { strategymanager } from '@sudoku/strategy/strategymanager'; + + export const usedHints = writable(0); -function createHints() { - let defaultHints = Infinity; +// 新增:用于存储提示文字的状态 +export const hintText = writable(''); +// 新增:用于存储提示文字的列表 +export const reasons_list = writable([]); - const hints = writable(Infinity); +export const hintstate = writable(true); - settings.subscribe(($settings) => { - if ($settings.hintsLimited) { - defaultHints = $settings.hints; - hints.update($hints => { - if ($hints > $settings.hints) return $settings.hints; +class Hints { + #hints; + #defaultHints; + #hintLevel; - return $hints; - }) - } else { - defaultHints = Infinity; - hints.set(Infinity); - } - }); + constructor() { + this.#defaultHints = Infinity; + this.#hints = writable(Infinity); - return { - subscribe: hints.subscribe, + settings.subscribe(($settings) => { + this.#hintLevel = $settings.hintLevel; + if ($settings.hintsLimited) { + this.#defaultHints = $settings.hints; + this.#hints.update(($hints) => { + if ($hints > $settings.hints) return $settings.hints; + return $hints; + }); + } else { + this.#defaultHints = Infinity; + this.#hints.set(Infinity); + } + }); + } - useHint() { - hints.update($hints => { - if ($hints > 0) { - usedHints.update($usedHints => $usedHints + 1); - return $hints - 1; + subscribe(callback) { + return this.#hints.subscribe(callback); + } + + /** + * 使用提示功能,返回提示内容。 + * @param {Array>} currentGrid 当前用户数独网格 + * @param {Object} pos 提示的坐标 { x, y } + * @returns {number|null} 提示的值,若无可用提示则返回 null + */ + useHint(currentGrid) { + let hintValue = []; + let selections = []; + let solve = []; + let reason = []; + this.#hints.update(($hints) => { + if ($hints > 0) { + usedHints.update(($usedHints) => $usedHints + 1); + selections = this.Candidates_Selection(currentGrid); + if (this.#hintLevel === 1) { // 一级提示:提示所有仅剩一个候选值的单元格 + if (selections[0] && selections[0].length > 0) { + solve = selections[0]; + } else if (selections[1] && selections[1].length > 0) { + solve = selections[1]; + } else { + solve = selections[2]; + } + } else if (this.#hintLevel === 2) { // 二级提示:提示所有剩两个候选值的单元格 + if (selections[1] && selections[1].length > 0) { + solve = selections[1]; + } else { + solve = selections[2]; + } + } else { // 三级提示:提示所有候选值 < 9 的单元格 + solve = selections[2]; + } + if (!solve || solve.length === 0) { + hintstate.set(false); + console.log("没有可用的解法了"); } + + return $hints - 1; + } + return $hints; // 提示次数为 0,不进行更新 + }); + return solve; + } + + Candidates_Selection(currentGrid) { + //三个等级提示的候选值列表 + let level_one_list = []; + let level_two_list = []; + let level_three_list = []; + + let candidates_and_reasons = strategymanager.inference(currentGrid); + let solvedsudoku = candidates_and_reasons[0]; + console.log("Solve:____", candidates_and_reasons[1]); - return 0; - }); - }, + reasons_list.set(candidates_and_reasons[1]); + for (let i = 0; i < solvedsudoku.length; i++) { + for (let j = 0; j < solvedsudoku[i].length; j++) { - reset() { - hints.set(defaultHints); - usedHints.set(0); + if (solvedsudoku[i][j].length === 1) { + level_one_list.push({ x: j, y: i, value: solvedsudoku[i][j] }); + } + if (solvedsudoku[i][j].length === 2) { + level_two_list.push({ x: j, y: i, value: solvedsudoku[i][j] }); + } + + if (solvedsudoku[i][j].length > 0) { + level_three_list.push({ x: j, y: i, value: solvedsudoku[i][j] }); + } + } } - }; + + + return [level_one_list, level_two_list, level_three_list]; + + } + + reset() { + this.#hints.set(this.#defaultHints); + usedHints.set(0); + } } -export const hints = createHints(); \ No newline at end of file +export const hints = new Hints(); diff --git a/src/node_modules/@sudoku/stores/state.js b/src/node_modules/@sudoku/stores/state.js new file mode 100644 index 00000000..a29f2aa2 --- /dev/null +++ b/src/node_modules/@sudoku/stores/state.js @@ -0,0 +1,218 @@ +import { writable } from 'svelte/store'; +import { hintStore } from '@sudoku/stores/hintStore'; +import { grid, userGrid } from '@sudoku/stores/grid'; + +// 用于存储所有提示内容 +let allHints = []; + +// 订阅 hintStore,获取所有提示数据 +hintStore.subscribe(hints => { + allHints = hints; // 保存整个 hintStore 列表 +}); + +/**************************************** + * 数独树节点类 + ****************************************/ +class SudokuNode { + constructor(id, grid) { + this.id = id; + this.grid = grid; + + // 父、子节点 + this.parent = null; + this.children = null; + + // 已经被证实无效的猜测 (比如本节点走到死胡同) + this.invalidhints = new Set(); + } + + /** + * 往本节点添加一个子节点 + * @param {SudokuNode} childNode + */ + addChild(childNode) { + childNode.parent = this; + this.children = childNode; + } +} + +/**************************************** + * 数独树管理器 + ****************************************/ +class SudokuTreeManager { + /** + * @param {object} initialGameState - 初始的数独盘面数据 + */ + constructor() { + // 用于生成节点 id + this.nodeCounter = 0; + // 如果不想用全局 nodeMap,也可以单独在这里维护 + this.localNodeMap = new Map(); + this.rootNode = null; + // currentNode 表示当前所处的节点(“游标”) + this.currentNode = null; + + // 栈,用于存储符合条件的节点 id + this.nodeStack = []; + + // 建立一个内部的 store,专门用来让 Svelte 追踪变化 + this._state = writable({ + currentNode: this.currentNode, + nodeStack: this.nodeStack, + canGoToParent: false, + canGoToSon: false, + isStackEmpty: true + }); + } + + get store() { + // 组件中会通过 manager.store 拿到这个可写 store + return this._state; + } + + // 内部方法:更新 store 状态 + _updateStore() { + this._state.set({ + currentNode: this.currentNode, + nodeStack: this.nodeStack, + canGoToParent: this.canGoToParent(), + canGoToSon: this.canGoToSon(), + isStackEmpty: this.isStackEmpty() + }); + } + + /** + * 内部方法:生成一个唯一节点 id + * @returns {string} + */ + _genNodeId() { + const id = `node-${this.nodeCounter}`; + this.nodeCounter++; + return id; + } + + generate(grid) { + // 创建根节点 + const rootId = this._genNodeId(); + this.rootNode = new SudokuNode(rootId, grid); + // 也放到本地 Map 里 + this.localNodeMap.set(rootId, this.rootNode); + // currentNode 表示当前所处的节点(“游标”) + this.currentNode = this.rootNode; + console.warn("Create root node"); + + this._updateStore(); // 每次修改 currentNode 后,都更新一下 + } + + /** + * 创建一个新的子节点,并挂到指定的 parentNode 下 + * @param {object} gameState - 新节点对应的游戏盘面 + * @param {object} parentNode - 父节点 + * @param {object} [options] - 包含 guessedCell, guessedValue 等 + * @returns {SudokuNode} - 新创建的子节点 + */ + createChildNode(newgrid, parentNode = this.currentNode) { + console.log("The parent grid before:", parentNode.grid); + const newId = this._genNodeId(); + const child = new SudokuNode(newId, newgrid); + parentNode.addChild(child); + + // 本地也登记一下 + this.localNodeMap.set(newId, child); + this.currentNode = child; + this._updateStore(); // 更新 store + console.log("Create child grid", grid); + console.log("The parent grid", parentNode.grid); + return child; + } + + /** + * 把 currentNode 切换到指定的节点 id + * 这就是所谓的“快速跳” + * @param {string} nodeId + */ + jumpToNode(nodeId) { + const targetNode = this.localNodeMap.get(nodeId); + if (!targetNode) { + console.error(`Node with id=${nodeId} not found in localNodeMap!`); + return; + } + this.currentNode = targetNode; + userGrid.setGrid(this.currentNode.grid); + console.log(`Jumped to node: ${nodeId}`); + + this._updateStore(); // 更新 store + } + + /** + * 回到父节点 (若有) + */ + canGoToParent() { + return this.currentNode && this.currentNode.parent !== null; + } + goToParent() { + if (this.currentNode && this.currentNode.parent) { + this.currentNode = this.currentNode.parent; + console.log("this.currentNode.grid:", this.currentNode.grid); + userGrid.setGrid(this.currentNode.grid); + + console.log(`Went back to parent: ${this.currentNode.id}`); + this._updateStore(); // 更新 store + } else { + console.warn("Current node has no parent!"); + } + } + + /** + * 回到子节点 (若有) + */ + canGoToSon() { + return this.currentNode && this.currentNode.children !== null; + } + goToSon() { + if (this.currentNode && this.currentNode.children) { + this.currentNode = this.currentNode.children; + console.log("this.currentNode.grid:", this.currentNode.grid); + userGrid.setGrid(this.currentNode.grid); + console.log(`Went back to children: ${this.currentNode.id}`); + this._updateStore(); // 更新 store + } else { + console.warn("Current node has no children!"); + } + } + + /** + * 演示:标记当前节点的某个猜测值为无效 + * @param {number} value + */ + markInvalidGuess(value) { + this.currentNode.invalidGuesses.add(value); + console.log(`Marked guess "${value}" as invalid in node ${this.currentNode.id}`); + } + + isStackEmpty() { + const empty = this.nodeStack.length === 0; + return empty; + } + + pushToStack() { + this.nodeStack.push(this.currentNode.id); + console.log(`Pushed node id "${this.currentNode.id}" to stack.`); + } + + /** + * 从栈中弹出最顶端的 id 并跳转到对应的节点 + */ + popFromStack() { + if (this.nodeStack.length === 0) { + console.warn("Stack is empty. Cannot pop."); + return; + } + const nodeId = this.nodeStack.pop(); + console.log(`Popped node id "${nodeId}" from stack.`); + this.jumpToNode(nodeId); + } + +} + +export const sudokutreemanager = new SudokuTreeManager(); diff --git a/src/node_modules/@sudoku/strategy/3DMedusaStrategy.js b/src/node_modules/@sudoku/strategy/3DMedusaStrategy.js new file mode 100644 index 00000000..ae863125 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/3DMedusaStrategy.js @@ -0,0 +1,732 @@ +import { Strategy } from '@sudoku/strategy/strategy.js'; +import {basicStragety} from '@sudoku/strategy/basicStrategy.js' +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; + +const BOX_SIZE = 3; +const SUDOKU_SIZE = 9; + +export class ThreeDMedusaStrategy extends Strategy { + constructor() { + super(4, "3D Medusa"); // "3D Medusa"是策略名称 + } + + apply(sudoku) { + // 获取所有可能候选值 + const [allCandidates, allReason] = basicStragety.apply(sudoku); + console.log("初始候选数:", JSON.stringify(allCandidates)); + + // 确定推理起始单元格 + const [inirow1, inicol1, inirow2, inicol2, num] = this.findOptimalMedusaStartPoint(allCandidates); + //const [inirow1, inicol1, inirow2, inicol2, num] = [1,6,2,6,9] + console.log([inirow1, inicol1, inirow2, inicol2]) + + // 构建强链接和弱链接 + const links = this.buildLinks(inirow1, inicol1, inirow2, inicol2, num, allCandidates); + console.log(links) + //console.log("构建的链接:", JSON.stringify(links)); + + // 应用3D Medusa规则 + //this.applyMedusaRules(links, allCandidates, allReason); + //console.log("最终候选数:", JSON.stringify(allCandidates)); + //return [allCandidates, allReason]; + const [updatedCandidates, updatedReason] = this.applyMedusaRules(links, allCandidates, allReason); + console.log("最终候选数:", JSON.stringify(updatedCandidates)); + + return [updatedCandidates, updatedReason]; + } + + + /** + * 寻找并返回一个适合作为美杜莎链起点的单元格对。 + * @param {Array>>} allCandidates - 包含每个单元格候选数的二维数组。 + * @returns {[number, number, number, number, number]} - 选中的起始单元格对的行和列索引及共享的候选数。 + */ + findIniUnit(allCandidates) { + const SUDOKU_SIZE = allCandidates.length; + + // 遍历所有单元格,寻找包含两个候选数的单元格作为潜在的起始点 + for (let row = 0; row < SUDOKU_SIZE; row++) { + for (let col = 0; col < SUDOKU_SIZE; col++) { + if (allCandidates[row][col].length === 2) { + const [num1, num2] = allCandidates[row][col]; + + // 检查行、列和九宫格内是否有其他单元格也包含相同的候选数之一 + // 行内查找 + for (let c = 0; c < SUDOKU_SIZE; c++) { + if (c !== col && allCandidates[row][c].includes(num1)) { + return [row, col, row, c, num1]; + } + if (c !== col && allCandidates[row][c].includes(num2)) { + return [row, col, row, c, num2]; + } + } + + // 列内查找 + for (let r = 0; r < SUDOKU_SIZE; r++) { + if (r !== row && allCandidates[r][col].includes(num1)) { + return [row, col, r, col, num1]; + } + if (r !== row && allCandidates[r][col].includes(num2)) { + return [row, col, r, col, num2]; + } + } + + // 九宫格内查找 + const boxRowStart = Math.floor(row / 3) * 3; + const boxColStart = Math.floor(col / 3) * 3; + for (let r = 0; r < 3; r++) { + for (let c = 0; c < 3; c++) { + let br = boxRowStart + r; + let bc = boxColStart + c; + if (br !== row && bc !== col && allCandidates[br][bc].includes(num1)) { + return [row, col, br, bc, num1]; + } + if (br !== row && bc !== col && allCandidates[br][bc].includes(num2)) { + return [row, col, br, bc, num2]; + } + } + } + } + } + } + + // 如果没有找到合适的起始单元格对,则返回默认值 + console.warn("未找到合适的起始单元格对"); + return [-1, -1, -1, -1, -1]; + } + + /** + * 寻找并返回一个最适合作为美杜莎链起点的单元格对。以二值格、强链接数量作为筛选标准。 + * @param {Array>>} allCandidates - 包含每个单元格候选数的二维数组。 + * @returns {[number, number, number, number, number]} - 选中的起始单元格对的行和列索引及共享的候选数。 + */ + findOptimalMedusaStartPoint(allCandidates) { + // 辅助函数:检查是否为二值单元格 + function isBivalueCell(candidates) { + return candidates.length === 2; + } + + // 辅助函数:查找强链接(行、列或九宫格内某候选数仅出现两次) + function findStrongLinks(unit, num) { + let positions = []; + for (let i = 0; i < unit.length; i++) { + if (unit[i].includes(num)) { + positions.push(i); + } + } + return positions.length === 2 ? positions : null; + } + + // 辅助函数:计算某个二值单元格的强链接条数 + function countStrongLinks(row, col) { + let links = 0; + const [num1, num2] = allCandidates[row][col]; + + // 行内的强链接 + [num1, num2].forEach(num => { + let positions = findStrongLinks(allCandidates[row], num); + if (positions && positions.length === 2) { + links++; + } + }); + + // 列内的强链接 + [num1, num2].forEach(num => { + let positions = []; + for (let r = 0; r < SUDOKU_SIZE; r++) { + if (allCandidates[r][col].includes(num)) { + positions.push(r); + } + } + if (positions.length === 2) { + links++; + } + }); + + // 九宫格内的强链接 + [num1, num2].forEach(num => { + let positions = []; + const boxRowStart = Math.floor(row / BOX_SIZE) * BOX_SIZE; + const boxColStart = Math.floor(col / BOX_SIZE) * BOX_SIZE; + for (let r = 0; r < BOX_SIZE; r++) { + for (let c = 0; c < BOX_SIZE; c++) { + let br = boxRowStart + r; + let bc = boxColStart + c; + if (allCandidates[br][bc].includes(num)) { + positions.push([br, bc]); + } + } + } + if (positions.length === 2) { + links++; + } + }); + + return links; + } + + // 收集所有二值单元格及其强链接条数 + let bivalueCells = []; + + for (let row = 0; row < SUDOKU_SIZE; row++) { + for (let col = 0; col < SUDOKU_SIZE; col++) { + if (isBivalueCell(allCandidates[row][col])) { + const strongLinksCount = countStrongLinks(row, col); + bivalueCells.push({ row, col, strongLinks: strongLinksCount }); + } + } + } + + // 根据强链接条数排序,选择最多的 + bivalueCells.sort((a, b) => b.strongLinks - a.strongLinks); + + // 如果没有找到二值单元格,返回null + if (bivalueCells.length === 0) { + return [-1,-1,-1,-1,-1]; + } + + // 获取强链接条数最多的二值单元格 + const bestBivalueCell = bivalueCells[0]; + const [num1, num2] = allCandidates[bestBivalueCell.row][bestBivalueCell.col]; + + // 返回最优起始点,格式化为 [row1, col1, row2, col2, sharedNum] + return [bestBivalueCell.row, bestBivalueCell.col, bestBivalueCell.row, bestBivalueCell.col, num1]; + } + + /** + * 构建美杜莎链,从给定的起始单元格对开始,使用迭代而非递归。 + * @param {number} inirow1 - 第一个起始单元格的行索引。 + * @param {number} inicol1 - 第一个起始单元格的列索引。 + * @param {number} inirow2 - 第二个起始单元格的行索引。 + * @param {number} inicol2 - 第二个起始单元格的列索引。 + * @param {number} sharedNum - 两个起始单元格共享的候选数。 + * @param {Array>>} allCandidates - 包含每个单元格候选数的二维数组。 + * @returns {Object} - 包含所有链接的对象。 + */ + buildLinks(inirow1, inicol1, inirow2, inicol2, sharedNum, allCandidates) { + if (inirow1 === -1 || inicol1 === -1 || inirow2 === -1 || inicol2 === -1 || sharedNum === -1) { + return {}; // 如果没有找到合适的起点,则返回空链接对象 + } + + const links = {}; + const colors = new Map(); // 使用Map来存储颜色分配 + const RED = 'red'; + const BLUE = 'blue'; + + // 初始化颜色:从起始单元格的共享候选数开始,分别设为红色和蓝色 + colors.set(`${inirow1},${inicol1},${sharedNum}`, RED); + //colors.set(`${inirow2},${inicol2},${sharedNum}`, BLUE); + + // 辅助函数:检查并添加链接 + function addLink(row, col, num, color) { + const key = `${row},${col},${num}`; + if (!links[key] || links[key].color===null) { + links[key] = { + row: row, + col: col, + num: num, + color: color, + linkedTo: [] + }; + } + return links[key]; + } + + // 队列用于迭代处理单元格和数字对 + const queue = [ + { row: inirow1, col: inicol1, num: sharedNum, color: RED }, + //{ row: inirow2, col: inicol2, num: sharedNum, color: BLUE } + ]; + + while (queue.length > 0) { + const { row, col, num, color } = queue.shift(); + const oppositeColor = color === RED ? BLUE : RED; + + // 添加当前链接 + const currentLink = addLink(row, col, num, color); + + // 强链接的搜索与添加 + // 同一单元格 + if (allCandidates[row][col].length === 2){ + let nextnum = -1; + if (allCandidates[row][col][0] !== num){ + nextnum = allCandidates[row][col][0]; + } + else { + nextnum = allCandidates[row][col][1]; + } + const nextCellKey = `${row},${col},${nextnum}`; + if (!colors.has(nextCellKey)) { + colors.set(nextCellKey, oppositeColor); + const nextLink = addLink(row, col, nextnum, oppositeColor); + currentLink.linkedTo.push(nextLink); + queue.push({ row: row, col: col, num: nextnum, color: oppositeColor }); + } + } + // 查找行、列、九宫格内的其他位置 + let count = 1; //同一单元中值为num的候选值的个数,恰好为2时才是强链接 + let nextrow = -1; let nextcol = -1; + let nextCellKey = `${-1},${-1},${-1}`; + // 行 + nextrow = row; + for (let i = 0; i < SUDOKU_SIZE; i++) { + if (i !== col && allCandidates[row][i].includes(num)) { + count = count + 1; + nextcol = i; + } + } + if (count === 2){ + nextCellKey = `${nextrow},${nextcol},${num}`; + if (!colors.has(nextCellKey)) { + colors.set(nextCellKey, oppositeColor); + const nextLink = addLink(nextrow, nextcol, num, oppositeColor); + currentLink.linkedTo.push(nextLink); + queue.push({ row: nextrow, col: nextcol, num: num, color: oppositeColor }); + } + } + // 列 + count = 1; + nextcol = col; + for (let i = 0; i < SUDOKU_SIZE; i++) { + if (i !== row && allCandidates[i][col].includes(num)) { + count = count + 1; + nextrow = i; + } + } + if (count === 2){ + nextCellKey = `${nextrow},${nextcol},${num}`; + if (!colors.has(nextCellKey)) { + colors.set(nextCellKey, oppositeColor); + const nextLink = addLink(nextrow, nextcol, num, oppositeColor); + currentLink.linkedTo.push(nextLink); + queue.push({ row: nextrow, col: nextcol, num: num, color: oppositeColor }); + } + } + // 九宫格 + count = 1; + const boxRowStart = Math.floor(row / BOX_SIZE) * BOX_SIZE; + const boxColStart = Math.floor(col / BOX_SIZE) * BOX_SIZE; + for (let r = 0; r < BOX_SIZE; r++) { + for (let c = 0; c < BOX_SIZE; c++) { + let br = boxRowStart + r; + let bc = boxColStart + c; + if ((br !== row || bc !== col) && allCandidates[br][bc].includes(num)) { + count = count + 1; + nextrow = br; + nextcol = bc; + } + } + } + if (count === 2){ + nextCellKey = `${nextrow},${nextcol},${num}`; + if (!colors.has(nextCellKey)) { + colors.set(nextCellKey, oppositeColor); + const nextLink = addLink(nextrow, nextcol, num, oppositeColor); + currentLink.linkedTo.push(nextLink); + queue.push({ row: nextrow, col: nextcol, num: num, color: oppositeColor }); + } + } + + // 检查当前单元格内的其他候选数(弱链接) + for (let otherNum of allCandidates[row][col]) { + if (otherNum !== num) { + const weakLinkKey = `${row},${col},${otherNum}`; + if (!links[weakLinkKey]) { + links[weakLinkKey] = { + row: row, + col: col, + num: otherNum, + color: null, // 弱链接不分配颜色 + linkedTo: [] + }; + } + } + } + } + + return links; + } + + /** + * 应用3D Medusa规则更新候选数。 + * @param {Object} links - 包含所有链接的对象。 + * @param {Array>>} allCandidates - 包含每个单元格候选数的二维数组。 + * @param {Array} allReason - 解题过程的理由记录。 + * @returns {[Array>>, Array]} - 更新后的候选数列表和解题理由。 + */ + applyMedusaRules(links, allCandidates, allReason) { + const RED = 'red'; + const BLUE = 'blue'; + + // 用于记录逻辑链的日志对象 + let chainLog = []; + + // 辅助函数:记录逻辑链 + function logChainAction(action, details) { + chainLog.push({ action, details }); + } + + // 规则1: 单元格中有两个相同颜色的候选值 + function ruleOne() { + for (const key in links) { + const link = links[key]; + if (link.color) { + const otherCandidateKeys = allCandidates[link.row][link.col].map(num => `${link.row},${link.col},${num}`).filter(k => k !== key); + for (let otherKey of otherCandidateKeys) { + const otherLink = links[otherKey]; + if (otherLink && otherLink.color === link.color) { + console.log(`规则1: 在(${link.row},${link.col})发现两个${link.color}颜色的候选数`); + return link.color; + } + } + } + } + return null; + } + + // 规则2: 同一行/列/九宫格中的两个相同候选值具有相同的颜色 + function ruleTwo() { + const seenColors = new Map(); + + for (const key in links) { + const link = links[key]; + if (link.color) { + // 分别处理行、列和九宫格 + const [rowColor, colColor, boxColor] = checkUnitColors(link.row, link.col, link.num, link.color); + + // 如果任意一个单元中发现了相同的颜色,则返回该颜色作为矛盾颜色 + if (rowColor || colColor || boxColor) { + console.log(`规则2: 在${rowColor ? '行' : ''}${colColor ? '列' : ''}${boxColor ? '九宫格' : ''}发现两个${link.color}颜色的候选数`); + return link.color; + } + } + } + return null; + } + + // 检查行、列、九宫格内的颜色冲突 + function checkUnitColors(row, col, num, color) { + let rowColor = false; + let colColor = false; + let boxColor = false; + + // 行内查找 + for (let c = 0; c < SUDOKU_SIZE; c++) { + if (c !== col && links[`${row},${c},${num}`]?.color === color) { + rowColor = true; + break; + } + } + + // 列内查找 + for (let r = 0; r < SUDOKU_SIZE; r++) { + if (r !== row && links[`${r},${col},${num}`]?.color === color) { + colColor = true; + break; + } + } + + // 九宫格内查找 + const boxRowStart = Math.floor(row / BOX_SIZE) * BOX_SIZE; + const boxColStart = Math.floor(col / BOX_SIZE) * BOX_SIZE; + for (let r = 0; r < BOX_SIZE; r++) { + for (let c = 0; c < BOX_SIZE; c++) { + let br = boxRowStart + r; + let bc = boxColStart + c; + if ((br !== row || bc !== col) && links[`${br},${bc},${num}`]?.color === color) { + boxColor = true; + break; + } + } + if (boxColor) break; + } + + return [rowColor, colColor, boxColor]; + } + + // 获取同一行、列或九宫格的所有键 + function getUnitKeys(row, col, num) { + const rowKeys = []; + const colKeys = []; + const boxKeys = []; + + // 行内查找 + for (let c = 0; c < SUDOKU_SIZE; c++) { + rowKeys.push(`${row},${c},${num}`); + } + + // 列内查找 + for (let r = 0; r < SUDOKU_SIZE; r++) { + colKeys.push(`${r},${col},${num}`); + } + + // 九宫格内查找 + const boxRowStart = Math.floor(row / BOX_SIZE) * BOX_SIZE; + const boxColStart = Math.floor(col / BOX_SIZE) * BOX_SIZE; + for (let r = 0; r < BOX_SIZE; r++) { + for (let c = 0; c < BOX_SIZE; c++) { + let br = boxRowStart + r; + let bc = boxColStart + c; + boxKeys.push(`${br},${bc},${num}`); + } + } + + return [...new Set([...rowKeys, ...colKeys, ...boxKeys])]; + } + + // 规则3: 单元格中有两种颜色的候选值和其他候选值 + function ruleThree() { + for (const key in links) { + const link = links[key]; + if (link.color) { + const cellLinks = Object.values(links).filter(l => l.row === link.row && l.col === link.col && l.color); + const colorsInCell = new Set(cellLinks.map(l => l.color)); + if (colorsInCell.size === 2) { + const [color1, color2] = colorsInCell; + const oppositeColor = color1 === RED ? BLUE : RED; + + // 移除其他颜色的候选数 + allCandidates[link.row][link.col] = allCandidates[link.row][link.col].filter(num => { + const candidateKey = `${link.row},${link.col},${num}`; + const candidateLink = links[candidateKey]; + return !candidateLink || candidateLink.color === color1 || candidateLink.color === color2; + }); + + // 记录解题理由 + allReason[link.row][link.col] = `3DMedusa 移除 (${link.row},${link.col}) 中非 ${color1}/${color2} 颜色的候选数` + console.log(`移除 (${link.row},${link.col}) 中非 ${color1}/${color2} 颜色的候选数`); + + // 记录逻辑链 + logChainAction('remove', { + position: [link.row, link.col], + reason: `因为存在两种颜色 ${color1} 和 ${color2}` + }); + } + } + } + } + + // 规则4: 某单元格中的某候选值,在它所在的行/列/九宫格中找到两种颜色的相同候选值 + function ruleFour() { + for (const key in links) { + const link = links[key]; + if (!link.color) { // 只考虑未着色的链接 + const unitKeys = getUnitKeys(link.row, link.col, link.num); + + // 获取单元内所有已着色且候选值相同的链接的颜色 + const colorsInUnit = new Set( + Object.values(links) + .filter(l => + l.color && // 已着色 + unitKeys.includes(`${l.row},${l.col},${l.num}`) && // 在同一单元内 + l.num === link.num // 候选值相同 + ) + .map(l => l.color) + ); + + if (colorsInUnit.size === 2) { // 如果在单元内找到了两种颜色的相同候选值 + allCandidates[link.row][link.col] = allCandidates[link.row][link.col].filter(num => num !== link.num); + + // 记录解题理由 + allReason[link.row][link.col] = `3DMedusa 移除 (${link.row},${link.col}) 中的 ${link.num} 因为在其所在单元找到了两种颜色的相同候选值`; + console.log(`移除 (${link.row},${link.col}) 中的 ${link.num} 因为在其所在单元找到了两种颜色的相同候选值`); + + // 记录逻辑链 + logChainAction('remove', { + num: link.num, + position: [link.row, link.col], + reason: `因为在所在单元找到了两种颜色的相同候选值` + }); + } + } + } + } + + // 规则5: 未着色的候选值可以在其他地方看到一个有色的候选值(共享行/列/九宫格),而在自己的单元格中可以看到相反的有色候选值 + function ruleFive() { + for (const key in links) { + const link = links[key]; + if (!link.color) { + const unitKeys = getUnitKeys(link.row, link.col, link.num); + const sameUnitLinks = Object.values(links).filter(l => unitKeys.includes(`${l.row},${l.col},${l.num}`)); + + const coloredLinks = sameUnitLinks.filter(l => l.color); + if (coloredLinks.length > 0) { + const oppositeColor = coloredLinks.some(l => l.color === RED) ? BLUE : RED; + const oppositeLinkInCell = Object.values(links).find(l => l.row === link.row && l.col === link.col && l.color === oppositeColor); + + if (oppositeLinkInCell) { + allCandidates[link.row][link.col] = allCandidates[link.row][link.col].filter(num => num !== link.num); + + // 记录解题理由 + allReason[link.row][link.col] = `3DMedusa 移除 (${link.row},${link.col}) 中的 ${link.num} 因为在同一单元看到了相反颜色 ${oppositeColor}` + console.log(`移除 (${link.row},${link.col}) 中的 ${link.num} 因为在同一单元看到了相反颜色 ${oppositeColor}`); + + // 记录逻辑链 + logChainAction('remove', { + num: link.num, + position: [link.row, link.col], + reason: `因为在同一单元看到了相反颜色 ${oppositeColor}` + }); + } + } + } + } + } + + // 规则6: 找到一个所有候选值都未着色的单元格,所有未着色的候选值都能看到相同颜色的对应候选值,则可以推断出另一颜色是正确的。 + function ruleSix() { + for (let row = 0; row < SUDOKU_SIZE; row++) { + for (let col = 0; col < SUDOKU_SIZE; col++) { + if (allCandidates[row][col].length > 1) { + const uncoloredCandidates = allCandidates[row][col].filter(num => !links[`${row},${col},${num}`] || !links[`${row},${col},${num}`].color); + + if (uncoloredCandidates.length === allCandidates[row][col].length) { + // 检查每个未着色候选值是否能看到相同颜色的对应候选值 + const colorMap = new Map(); + for (const num of uncoloredCandidates) { + const unitKeys = getUnitKeys(row, col, num); + const seenColors = new Set(unitKeys.map(key => links[key]?.color).filter(color => color)); + + if (seenColors.size === 1) { + const [color] = seenColors; + colorMap.set(num, color); + } else { + break; + } + } + + // 如果所有候选值都能看到相同颜色的对应候选值 + if (colorMap.size === uncoloredCandidates.length) { + const oppositeColor = [...new Set([RED, BLUE])].find(color => ![...colorMap.values()].includes(color)); + + // 确定该单元格的唯一解决方案 + const solutionNum = uncoloredCandidates.find(num => colorMap.get(num) !== oppositeColor); + allCandidates[row][col] = [solutionNum]; + + // 记录解题理由 + allReason[link.row][link.col] = `确认 (${row},${col}) 的唯一解为 ${solutionNum} 因为其他颜色都看到了 ${oppositeColor}` + //allReason.push(`确认 (${row},${col}) 的唯一解为 ${solutionNum} 因为其他颜色都看到了 ${oppositeColor}`); + + // 记录逻辑链 + logChainAction('confirm', { + num: solutionNum, + position: [row, col], + reason: `因为其他颜色都看到了 ${oppositeColor}` + }); + + return true; + } + } + } + } + } + return false; + } + + // 应用规则 + function applyRules() { + let contradictoryColor = ruleOne(); + if (contradictoryColor) { + updateCandidatesBasedOnContradiction(contradictoryColor, links, allCandidates, allReason); + } + + contradictoryColor = ruleTwo(); + if (contradictoryColor) { + updateCandidatesBasedOnContradiction(contradictoryColor, links, allCandidates, allReason); + } + + ruleThree(); + ruleFour(); + //ruleFive(); + //ruleSix(); + } + + // 根据找到的矛盾更新候选数 + function updateCandidatesBasedOnContradiction(contradictoryColor, links, allCandidates, allReason) { + for (const key in links) { + const link = links[key]; + if (link.color === contradictoryColor) { + allCandidates[link.row][link.col] = allCandidates[link.row][link.col].filter(num => num !== link.num); + + // 记录解题理由 + allReason[link.row][link.col] = `3DMedusa 移除 ${link.num} 在 (${link.row},${link.col}) 因为找到了${contradictoryColor}颜色的矛盾` + console.log(`移除 ${link.num} 在 (${link.row},${link.col}) 因为找到了${contradictoryColor}颜色的矛盾`); + + // 记录逻辑链 + logChainAction('remove', { + num: link.num, + position: [link.row, link.col], + reason: `因为找到了${contradictoryColor}颜色的矛盾` + }); + } + } + } + + // 执行规则 + applyRules(); + + // 打印推理逻辑链 + //console.log("最终确定唯一候选值的逻辑链:", JSON.stringify(chainLog, null, 2)); + + // 返回更新后的候选数列表和解题理由 + return [allCandidates, allReason]; + } +} + +export const threeDMedusaStrategy = new ThreeDMedusaStrategy(); + +/* +// 调试信息 +let sudoku = [ + [0, 0, 0, 3, 0, 0, 0, 8, 0], + [5, 0, 0, 0, 0, 1, 0, 0, 3], + [8, 0, 6, 0, 4, 0, 0, 0, 0], + [4, 0, 0, 8, 3, 0, 0, 0, 2], + [0, 0, 2, 0, 0, 0, 3, 0, 0], + [9, 0, 0, 0, 7, 2, 0, 0, 1], + [0, 0, 0, 0, 6, 0, 1, 0, 7], + [7, 0, 0, 1, 0, 0, 0, 0, 4], + [0, 9, 0, 0, 0, 5, 0, 0, 0], +]; + +let sudoku1 = [ + [0, 9, 3, 8, 2, 4, 5, 6, 0], + [0, 8, 5, 6, 0, 0, 0, 0, 2], + [2, 0, 6, 0, 7, 5, 0, 0, 8], + [3, 2, 1, 7, 6, 9, 8, 4, 5], + [0, 0, 0, 2, 5, 8, 3, 0, 0], + [5, 7, 8, 0, 4, 0, 2, 9, 6], + [8, 5, 0, 0, 1, 6, 7, 2, 3], + [0, 0, 7, 0, 8, 2, 6, 5, 0], + [0, 0, 2, 5, 0, 7, 1, 8, 0], +]; + +let sudoku2 = [ + [0, 9, 3, 8, 2, 4, 5, 6, 0], + [0, 8, 5, 6, 3, 0, 9, 0, 2], + [2, 1, 6, 0, 7, 5, 4, 0, 8], + [3, 2, 1, 7, 6, 9, 8, 4, 5], + [0, 0, 0, 2, 5, 8, 3, 0, 0], + [5, 7, 8, 0, 4, 0, 2, 9, 6], + [8, 5, 0, 0, 1, 6, 7, 2, 3], + [0, 4, 7, 0, 8, 2, 6, 5, 0], + [0, 0, 2, 5, 9, 7, 1, 8, 0], +]; + +let sudoku3 = [ + [7, 9, 3, 8, 2, 4, 5, 6, 1], + [4, 8, 5, 6, 3, 1, 9, 7, 2], + [2, 1, 6, 9, 7, 5, 4, 3, 8], + [3, 2, 1, 7, 6, 9, 8, 4, 5], + [9, 6, 4, 2, 5, 8, 3, 1, 7], + [5, 7, 8, 1, 4, 3, 2, 9, 6], + [8, 5, 9, 4, 1, 6, 7, 2, 3], + [1, 4, 7, 3, 8, 2, 6, 5, 9], + [6, 3, 2, 5, 9, 7, 1, 8, 4], +]; + +const [allCandidates, allReason] = threeDMedusaStrategy.apply(sudoku3); +console.log(allCandidates) +//console.log(allReason) +*/ \ No newline at end of file diff --git a/src/node_modules/@sudoku/strategy/basicStrategy.js b/src/node_modules/@sudoku/strategy/basicStrategy.js new file mode 100644 index 00000000..888bb111 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/basicStrategy.js @@ -0,0 +1,237 @@ +//import { get_all_dirty_from_scope } from 'svelte/internal'; +import { Strategy } from './strategy.js'; +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; + + + + +const BOX_SIZE = 3; +const SUDOKU_SIZE = 9; + + + +export class BasicStrategy extends Strategy { + constructor() { + super(0,"Basic Strategy"); + } + + + apply(sudoku) { + const allCandidates = Array.from({ length: SUDOKU_SIZE }, () => Array(SUDOKU_SIZE).fill([])); + const allReason = Array.from({ length: SUDOKU_SIZE }, () => Array(SUDOKU_SIZE).fill([])); + for (let row = 0; row < SUDOKU_SIZE; row++) { + for (let column = 0; column < SUDOKU_SIZE; column++) { + if (sudoku[row][column] === 0) { // 检查单元格是否为空 + const candidates = this.getCandidateNumber(sudoku, row, column); + allCandidates[row][column] = candidates; // 直接赋值,而不是 push + allReason[row][column] = allReason[row][column].concat(['Basic SUDOKU rules.']).filter( + element => !Array.isArray(element) || element.length !== 0); + } + } + } + // clue是线索,是文本list的形式 + return [allCandidates, allReason]; + } + + + + getRow(sudoku, row) { + let nonZeroNumber = []; + for(let column = 0; column < SUDOKU_SIZE; column++) { + if (sudoku[row][column] !== 0) { + nonZeroNumber.push(sudoku[row][column]); + } + } + return nonZeroNumber; + } + + + getColumn(sudoku, column) { + let nonZeroNumbers = []; + for (let row = 0; row < SUDOKU_SIZE; row++) { + if (sudoku[row][column] !== 0) { + nonZeroNumbers.push(sudoku[row][column]); + } + } + return nonZeroNumbers; + } + + + getBox(sudoku, row, column) { + let nonZeroNumbers = []; + const boxStartRow = Math.floor(row / BOX_SIZE) * BOX_SIZE; + const boxStartColumn = Math.floor(column / BOX_SIZE) * BOX_SIZE; + for (let i = 0; i < BOX_SIZE; i++) { + for (let j = 0; j < BOX_SIZE; j++) { + if (sudoku[boxStartRow + i][boxStartColumn + j] !== 0) { + nonZeroNumbers.push(sudoku[boxStartRow + i][boxStartColumn + j]); + } + } + } + return nonZeroNumbers; + } + + + isValid(sudoku, number, row, column) { + // 获取行中的非零数字 + const rowNumbers = this.getRow(sudoku, row); + // 获取列中的非零数字 + const columnNumbers = this.getColumn(sudoku, column); + // 获取九宫格中的非零数字 + const boxNumbers = this.getBox(sudoku, row, column); + + // 检查number是否在行、列或九宫格中的任何一个数组中 + return (rowNumbers.indexOf(number) === -1 && + columnNumbers.indexOf(number) === -1 && + boxNumbers.indexOf(number) === -1); + } + + + getCandidateNumber(sudoku, row, column) { + const candidates = []; + // 遍历所有可能的数字 + for (let number = 1; number <= SUDOKU_SIZE; number++) { + // 检查该数字是否在指定位置有效 + if (this.isValid(sudoku, number, row, column)) { + candidates.push(number); + } + } + return candidates; + } + + + + // 对于一行内除了特定位置position_list外消除特定的候选值candidate_list + removeInRow(allCandidates, allReason, position_list, candidate_list, row, strategy_name) { + for (let col = 0; col < SUDOKU_SIZE; col ++) { + if(!this.isPositionInList([row, col], position_list)) { + // 仅仅保留不在candidate_list内的候选数字 + allCandidates[row][col] = allCandidates[row][col].filter(element => !candidate_list.includes(element)); + allReason[row][col] = allReason[row][col].concat([`因为策略${strategy_name}, 候选值${candidate_list}被删除`]).filter( + element => !Array.isArray(element) || element.length !== 0); + } + } + } + + + + // 对于一列内除了特定位置position_list外消除特定的候选值candidate_list + removeInCol(allCandidates, allReason, position_list, candidate_list, col, strategy_name) { + for (let row = 0; row < SUDOKU_SIZE; row ++) { + if(!this.isPositionInList([row, col], position_list)) { + // 仅仅保留不在candidate_list内的候选数字 + allCandidates[row][col] = allCandidates[row][col].filter(element => !candidate_list.includes(element)); + allReason[row][col] = allReason[row][col].concat([`因为策略${strategy_name}, 候选值${candidate_list}被删除`]).filter( + element => !Array.isArray(element) || element.length !== 0); + } + } + } + + + // 对于一个九宫格内除了特定位置position_list外消除特定的候选值candidate_list + removeInBox(allCandidates, allReason, position_list, candidate_list, boxRow, boxCol, strategy_name) { + for (let boxIndex = 0; boxIndex < BOX_SIZE*BOX_SIZE; boxIndex ++) { + const row = boxRow*BOX_SIZE + Math.floor(boxIndex / BOX_SIZE); + const col = boxCol*BOX_SIZE + boxIndex % BOX_SIZE; + if(!this.isPositionInList([row, col], position_list)) { + // 仅仅保留不在candidate_list内的候选数字 + allCandidates[row][col] = allCandidates[row][col].filter(element => !candidate_list.includes(element)); + allReason[row][col] = allReason[row][col].concat([`因为策略${strategy_name}, 候选值${candidate_list}被删除`]).filter( + element => !Array.isArray(element) || element.length !== 0); + } + } + } + + + // 用于解决position位置是否在位置列表position_list中作为其中一个元素 + isPositionInList(position, position_list) { + return position_list.some(subArray => + position.length === subArray.length && + position.every((element, index) => element === subArray[index]) + ); + } + + + isSameRow(position_list) { + if(position_list.length < 2) { + return false; + } + const row = position_list[0][0]; + for (let i = 1; i < position_list.legth; i++) { + if(position_list[i][0] !== row) { + return false; + } + } + return true; + } + + + + isSameCol(position_list) { + if(position_list.length < 2) { + return false; + } + const col = position_list[0][1]; + for (let i = 1; i < position_list.legth; i++) { + if(position_list[i][1] !== col) { + return false; + } + } + return true; + } + + + + + isSameBox(position_list) { + if(position_list.length < 2) { + return false; + } + let boxRow = []; + let boxCol = []; + for (let i = 0; i < position_list.length; i++) { + boxRow.push(Math.floor(position_list[i][0] / BOX_SIZE) * BOX_SIZE); + boxCol.push(Math.floor(position_list[i][1] / BOX_SIZE) * BOX_SIZE); + } + for (let i = 0; i < position_list.length; i++) { + if (boxRow[i] !== boxRow[0] || boxCol[i] !== boxCol[0]) { + return false; + } + } + return true; + } + + + + + getAmountCandidate(allCandidates) { + let amount = Array.from({ length: SUDOKU_SIZE }, () => Array(SUDOKU_SIZE).fill(null)); + for (let row = 0; row < SUDOKU_SIZE; row ++) { + for (let col = 0; col < SUDOKU_SIZE; col++) { + amount[row][col] = allCandidates[row][col].length; + } + } + return amount; + } + + + isSameAmount(oldAmount, newAmount) { + for (let row = 0; row < SUDOKU_SIZE; row ++) { + for (let col = 0; col < SUDOKU_SIZE; col++) { + if(oldAmount[row][col] !== newAmount[row][col]) { + return false; + } + } + } + return true; + } + + +} + + +export const basicStragety = new BasicStrategy() + + + + diff --git a/src/node_modules/@sudoku/strategy/hiddenPair.js b/src/node_modules/@sudoku/strategy/hiddenPair.js new file mode 100644 index 00000000..5564a153 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/hiddenPair.js @@ -0,0 +1,202 @@ +import { Strategy } from '@sudoku/strategy/strategy.js'; +import {basicStragety} from '@sudoku/strategy/basicStrategy.js' +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; + + +const BOX_SIZE = 3; +const SUDOKU_SIZE = 9; + + + +export class HiddenPairStrategy extends Strategy{ + constructor() { + super(3, "Hidden Pair"); + } + + apply(sudoku) { + const [allCandidates, allReason] = basicStragety.apply(sudoku); + let change = true; + while(change) { + let oldAmount = basicStragety.getAmountCandidate(allCandidates); + // 检查行 + for (let row = 0; row < SUDOKU_SIZE; row++) { + this.findHiddenPairInRow(allCandidates, row, allReason); + } + // 检查列 + for (let col = 0; col < SUDOKU_SIZE; col++) { + this.findHiddenPairInCol(allCandidates, col, allReason); + } + // 检查九宫格 + for (let boxRow = 0; boxRow < SUDOKU_SIZE/BOX_SIZE; boxRow++) { + for (let boxCol = 0; boxCol < SUDOKU_SIZE/BOX_SIZE; boxCol++) { + this.findHiddenPairInBox(allCandidates, boxRow, boxCol, allReason); + } + } + let newAmount = basicStragety.getAmountCandidate(allCandidates); + change = !basicStragety.isSameAmount(oldAmount, newAmount); + } + return [allCandidates, allReason]; + } + + + + // 寻找同一行的hidden pair + findHiddenPairInRow(allCandidates, row, allReason) { + const candidateCounts = Array.from({ length: SUDOKU_SIZE + 1 }, () => new Set()); + // 统计每个数字在行中的出现位置 + for (let col = 0; col < SUDOKU_SIZE; col++) { + allCandidates[row][col].forEach(num => candidateCounts[num].add(col)); + } + // 查找只在两个单元格中出现的数字对 + for (let num1 = 1; num1 <= SUDOKU_SIZE; num1++) { + for (let num2 = num1 + 1; num2 <= SUDOKU_SIZE; num2++){ + const combineSet = new Set([...candidateCounts[num1], ...candidateCounts[num2]]); + // 如果只有两个位置存在这两个候选值,就构成hidden pair + //if (combineSet.size === 2 && candidateCounts[num1].size >= 1 && candidateCounts[num2].size >= 1) { + + if (combineSet.size === 2 && candidateCounts[num1].size === 2 && candidateCounts[num2].size === 2) { + // 将者两个位置赋值为nake pair,清除其他无关候选值 + const combineList = [...combineSet]; + allCandidates[row][combineList[0]] = allCandidates[row][combineList[0]].filter(element => element === num1 || element === num2); + allCandidates[row][combineList[1]] = allCandidates[row][combineList[1]].filter(element => element === num1 || element === num2); + allReason[row][combineList[0]] = allReason[row][combineList[0]].concat([`[${row}, ${combineList[0]}] 和 [${row}, ${combineList[1]}] 是一对行Hidden Pair, 只保留候选值${allCandidates[row][combineList[0]]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[row][combineList[0]] = [...new Set(allReason[row][combineList[0]])]; + allReason[row][combineList[1]] = allReason[row][combineList[1]].concat([`[${row}, ${combineList[0]}] 和 [${row}, ${combineList[1]}] 是一对行Hidden Pair, 只保留候选值${allCandidates[row][combineList[1]]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[row][combineList[1]] = [...new Set(allReason[row][combineList[1]])]; + // 将同一行的无关元素全部清除 + basicStragety.removeInRow(allCandidates, allReason, [[row, combineList[0]], [row, combineList[1]]], + [num1, num2], row, this.description); + // 如果同时满足在同一个九宫格,同时消除九宫格里面的数字 + if (basicStragety.isSameBox([[row, combineList[0]], [row, combineList[1]]])) { + basicStragety.removeInBox(allCandidates, allReason, [[row, combineList[0]], [row, combineList[1]]], + [num1, num2], Math.floor(row / BOX_SIZE), Math.floor(combineList[0] / BOX_SIZE), this.description); + } + } + } + } + } + + + + // 寻找同一列的hidden pair + findHiddenPairInCol(allCandidates, col, allReason) { + const candidateCounts = Array.from({ length: SUDOKU_SIZE + 1 }, () => new Set()); + // 统计每个数字在行中的出现位置 + for (let row = 0; row < SUDOKU_SIZE; row++) { + allCandidates[row][col].forEach(num => candidateCounts[num].add(row)); + } + // 查找只在两个单元格中出现的数字对 + for (let num1 = 1; num1 <= SUDOKU_SIZE; num1++) { + for (let num2 = num1 + 1; num2 <= SUDOKU_SIZE; num2++){ + const combineSet = new Set([...candidateCounts[num1], ...candidateCounts[num2]]); + // 如果只有两个位置存在这两个候选值,就构成hidden pair + //if (combineSet.size === 2 && candidateCounts[num1].size >= 1 && candidateCounts[num2].size >= 1) { + if (combineSet.size === 2 && candidateCounts[num1].size === 2 && candidateCounts[num2].size === 2) { + // 将者两个位置赋值为nake pair,清除其他无关候选值 + const combineList = [...combineSet]; + allCandidates[combineList[0]][col] = allCandidates[combineList[0]][col].filter(element => element === num1 || element === num2); + allCandidates[combineList[1]][col] = allCandidates[combineList[1]][col].filter(element => element === num1 || element === num2); + allReason[combineList[0]][col] = allReason[combineList[0]][col].concat([`[${combineList[0]}, ${col}] 和 [${combineList[1]}, ${col}] 是一对列Hidden Pair, 只保留候选值${allCandidates[combineList[0]][col]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[combineList[0]][col] = [...new Set(allReason[combineList[0]][col])]; + allReason[combineList[1]][col] = allReason[combineList[1]][col].concat([`[${combineList[0]}, ${col}] 和 [${combineList[1]}, ${col}] 是一对列Hidden Pair, 只保留候选值${allCandidates[combineList[1]][col]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[combineList[1]][col] = [...new Set(allReason[combineList[1]][col])]; + // 将同一列的无关元素全部清除 + basicStragety.removeInCol(allCandidates, allReason, [[combineList[0], col], [combineList[1], col]], + [num1, num2], col, this.description); + // 如果同时满足在同一个九宫格,同时消除九宫格里面的数字 + if (basicStragety.isSameBox([[combineList[0], col], [combineList[1], col]])) { + basicStragety.removeInBox(allCandidates, allReason, [[combineList[0], col], [combineList[1], col]], + [num1, num2], Math.floor(combineList[0] / BOX_SIZE), Math.floor(col / BOX_SIZE), this.description); + } + } + } + } + + } + + + + // 寻找同一个九宫格的hidden pair + findHiddenPairInBox(allCandidates, boxRow, boxCol, allReason) { + const candidateCounts = Array.from({ length: SUDOKU_SIZE + 1 }, () => new Set()); + // 统计每个数字在行中的出现位置 + for (let boxIndex = 0; boxIndex < BOX_SIZE*BOX_SIZE; boxIndex ++) { + let row = boxRow*BOX_SIZE + Math.floor(boxIndex / BOX_SIZE); + let col = boxCol*BOX_SIZE + boxIndex % BOX_SIZE; + allCandidates[row][col].forEach(num => candidateCounts[num].add([row, col])); + } + // 查找只在两个单元格中出现的数字对 + for (let num1 = 1; num1 <= SUDOKU_SIZE; num1++) { + for (let num2 = num1 + 1; num2 <= SUDOKU_SIZE; num2++){ + const combineSet = new Set([...candidateCounts[num1], ...candidateCounts[num2]]); + // 如果只有两个位置存在这两个候选值,就构成hidden pair + //if (combineSet.size === 2 && candidateCounts[num1].size >= 1 && candidateCounts[num2].size >= 1) { + if (combineSet.size === 2 && candidateCounts[num1].size === 2 && candidateCounts[num2].size === 2) { + // 将者两个位置赋值为nake pair,清除其他无关候选值 + const combineList = [...combineSet]; + allCandidates[combineList[0][0]][combineList[0][1]] = allCandidates[combineList[0][0]][combineList[0][1]].filter(element => element === num1 || element === num2); + allCandidates[combineList[1][0]][combineList[1][1]] = allCandidates[combineList[1][0]][combineList[1][1]].filter(element => element === num1 || element === num2); + allReason[combineList[0][0]][combineList[0][1]] = allReason[combineList[0][0]][combineList[0][1]].concat([`[${combineList[0][0]}, ${combineList[0][1]}] 和 [${combineList[1][0]}, ${combineList[1][1]}] 是一对宫Hidden Pair, 只保留候选值${allCandidates[combineList[0][0]][combineList[0][1]]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[combineList[0][0]][combineList[0][1]] = [...new Set(allReason[combineList[0][0]][combineList[0][1]])]; + allReason[combineList[1][0]][combineList[1][1]] = allReason[combineList[1][0]][combineList[1][1]].concat([`[${combineList[0][0]}, ${combineList[0][1]}] 和 [${combineList[1][0]}, ${combineList[1][1]}] 是一对宫Hidden Pair, 只保留候选值${allCandidates[combineList[1][0]][combineList[1][1]]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[combineList[1][0]][combineList[1][1]]= [...new Set(allReason[combineList[1][0]][combineList[1][1]])]; + // 将同一九宫格的无关元素全部清除 + basicStragety.removeInBox(allCandidates, allReason, [[combineList[0][0],combineList[0][1]], [combineList[1][0], combineList[1][1]]], + [num1, num2], boxRow, boxCol, this.description); + // 如果同时满足在同一个行,同时消除这一行里面的数字 + if (basicStragety.isSameRow([[combineList[0][0],combineList[0][1]], [combineList[1][0], combineList[1][1]]])) { + basicStragety.removeInRow(allCandidates, allReason, [[combineList[0][0],combineList[0][1]], [combineList[1][0], combineList[1][1]]], + [num1, num2], combineList[0][0], this.description); + } + // 如果同时满足在同一个列,同时消除这一列里面的数字 + if (basicStragety.isSameCol([[combineList[0][0],combineList[0][1]], [combineList[1][0], combineList[1][1]]])) { + basicStragety.removeInCol(allCandidates, allReason, [[combineList[0][0],combineList[0][1]], [combineList[1][0], combineList[1][1]]], + [num1, num2], combineList[0][1], this.description); + } + + } + } + } + } +} + + + +export const hiddenPairStrategy = new HiddenPairStrategy() + + + +/* +// 调试信息 +let sudoku = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [9, 0, 4, 6, 0, 7, 0, 0, 0], + [0, 7, 6, 8, 0, 4, 1, 0, 0], + [3, 0, 9, 7, 0, 1, 0, 8, 0], + [7, 0, 8, 0, 0, 0, 3, 0, 1], + [0, 5, 1, 3, 0, 8, 7, 0, 2], + [0, 0, 7, 5, 0, 2, 6, 1, 0], + [0, 0, 5, 4, 0, 3, 2, 0, 8], + [0, 0, 0, 0, 0, 0, 0, 0, 0], +]; +let sudoku1 = [ + [7, 2, 0, 4, 0, 8, 0, 3, 0], + [0, 8, 0, 0, 0, 0, 0, 4, 7], + [4, 0, 1, 0, 7, 6, 8, 0, 2], + [8, 1, 0, 7, 3, 9, 0, 0, 0], + [0, 0, 0, 8, 5, 1, 0, 0, 0], + [0, 0, 0, 2, 6, 4, 0, 8, 0], + [2, 0, 9, 6, 8, 0, 4, 1, 3], + [3, 4, 0, 0, 0, 0, 0, 0, 8], + [1, 6, 8, 9, 4, 3, 2, 7, 5], +]; + +console.log(hiddenPairStrategy.apply(sudoku1)); +*/ + diff --git a/src/node_modules/@sudoku/strategy/lastRemainStrategy.js b/src/node_modules/@sudoku/strategy/lastRemainStrategy.js new file mode 100644 index 00000000..87a819c3 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/lastRemainStrategy.js @@ -0,0 +1,100 @@ +import { Strategy } from '@sudoku/strategy/strategy.js'; +import {basicStragety} from '@sudoku/strategy/basicStrategy.js' +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; + + +const BOX_SIZE = 3; +const SUDOKU_SIZE = 9; + + + +export class LastRemianStrategy extends Strategy { + constructor() { + super(1, "Last Remian"); + } + + + apply(sudoku) { + const [allCandidates, allReason] = basicStragety.apply(sudoku); + // 检查行 + for (let row = 0; row < SUDOKU_SIZE; row++) { + this.findLastRemainInRow(allCandidates, row, allReason); + } + // 检查列 + for (let col = 0; col < SUDOKU_SIZE; col++) { + this.findLastRemainInCol(allCandidates, col, allReason); + } + // 检查九宫格 + for (let boxRow = 0; boxRow < SUDOKU_SIZE/BOX_SIZE; boxRow++) { + for (let boxCol = 0; boxCol < SUDOKU_SIZE/BOX_SIZE; boxCol++) { + this.findLastRemainInBox(allCandidates, boxRow, boxCol, allReason); + } + } + return [allCandidates, allReason]; + + } + + + // 寻找一行内的剩余数字 + findLastRemainInRow(allCandidates, row, allReason) { + // 对每一个数字进行检查 + for (let candidate = 1; candidate <= SUDOKU_SIZE; candidate++){ + let position = [] + for (let col = 0; col < SUDOKU_SIZE; col ++) { + if( allCandidates[row][col].length > 0 && allCandidates[row][col].includes(candidate)) { + position.push(col); + } + + } + if(position.length === 1) { + allCandidates[row][position[0]] = [candidate]; + allReason[row][position[0]] = [`Last Remaining in row. The last remaining candidate is ${candidate}`] + } + } + } + + + // 寻找一列内的剩余数字 + findLastRemainInCol(allCandidates, col, allReason) { + // 对每一个数字进行检查 + for (let candidate = 1; candidate <= SUDOKU_SIZE; candidate++){ + let position = [] + for (let row = 0; row < SUDOKU_SIZE; row ++) { + if( allCandidates[row][col].length > 0 && allCandidates[row][col].includes(candidate)) { + position.push(row); + } + } + if(position.length === 1) { + allCandidates[position[0]][col] = [candidate]; + allReason[position[0]][col] = [`Last Remaining in column. The last remaining candidate is ${candidate}`] + } + } + } + + + // 寻找一个九宫格内的剩余数字 + findLastRemainInBox(allCandidates, boxRow, boxCol, allReason) { + // 对每一个数字进行检查 + for (let candidate = 1; candidate <= SUDOKU_SIZE; candidate++){ + let position = [] + for (let boxIndex = 0; boxIndex < BOX_SIZE*BOX_SIZE; boxIndex ++) { + let row = boxRow*BOX_SIZE + Math.floor(boxIndex / BOX_SIZE); + let col = boxCol*BOX_SIZE + boxIndex % BOX_SIZE; + if( allCandidates[row][col].length > 0 && allCandidates[row][col].includes(candidate)) { + position.push([row, col]); + } + } + if(position.length === 1) { + allCandidates[position[0][0]][position[0][1]] = [candidate] + allReason[position[0][0]][position[0][1]] = [`Last Remaining in box. The last remaining candidate is ${candidate}`] + } + } + } +} + + + +export const lastRemianStrategy = new LastRemianStrategy() + + + diff --git a/src/node_modules/@sudoku/strategy/nakePair.js b/src/node_modules/@sudoku/strategy/nakePair.js new file mode 100644 index 00000000..bf442045 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/nakePair.js @@ -0,0 +1,157 @@ +import { Strategy } from '@sudoku/strategy/strategy.js'; +import {basicStragety} from '@sudoku/strategy/basicStrategy.js' +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; + + +const BOX_SIZE = 3; +const SUDOKU_SIZE = 9; + + + +export class NakePairStrategy extends Strategy { + constructor() { + super(2, "Nake Pair"); + } + + + apply(sudoku) { + const [allCandidates, allReason] = basicStragety.apply(sudoku); + let change = true; + while(change) { + let oldAmount = basicStragety.getAmountCandidate(allCandidates); + // 检查行 + for (let row = 0; row < SUDOKU_SIZE; row++) { + this.findNakePairInRow(allCandidates, row, allReason); + } + // 检查列 + for (let col = 0; col < SUDOKU_SIZE; col++) { + this.findNakePairInCol(allCandidates, col, allReason); + } + // 检查九宫格 + for (let boxRow = 0; boxRow < SUDOKU_SIZE/BOX_SIZE; boxRow++) { + for (let boxCol = 0; boxCol < SUDOKU_SIZE/BOX_SIZE; boxCol++) { + this.findNakePairInBox(allCandidates, boxRow, boxCol, allReason); + } + } + let newAmount = basicStragety.getAmountCandidate(allCandidates); + change = !basicStragety.isSameAmount(oldAmount, newAmount); + } + return [allCandidates, allReason]; + } + + + + // 寻找同一行的nake pair + findNakePairInRow(allCandidates, row, allReason) { + for (let firstCol = 0; firstCol !Array.isArray(element) || element.length !== 0); + allReason[row][firstCol] = [...new Set(allReason[row][firstCol])]; + allReason[row][nextCol] = allReason[row][nextCol].concat([`[${row}, ${firstCol}] 和 [${row}, ${nextCol}] 是一对行Naked Pair, 候选值为${allCandidates[row][nextCol]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[row][nextCol] = [...new Set(allReason[row][nextCol])]; + basicStragety.removeInRow(allCandidates, allReason, [[row, firstCol], [row, nextCol]], + [allCandidates[row][firstCol][0], allCandidates[row][firstCol][1]], row, this.description); + } + } + } + } + } + + + // 寻找同一列的nake pair + findNakePairInCol(allCandidates, col, allReason) { + for (let firstRow = 0; firstRow !Array.isArray(element) || element.length !== 0); + allReason[firstRow][col] = [...new Set(allReason[firstRow][col])]; + allReason[nextRow][col] = allReason[nextRow][col].concat([`[${firstRow}, ${col}] 和 [${nextRow}, ${col}] 是一对列Naked Pair, 候选值为${allCandidates[nextRow][col]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[nextRow][col] = [...new Set(allReason[nextRow][col])]; + basicStragety.removeInCol(allCandidates, allReason, [[firstRow, col], [nextRow, col]], + [allCandidates[firstRow][col][0], allCandidates[firstRow][col][1]], col, this.description); + + } + } + } + } + } + + + // 寻找同一个九宫格的nake pair + findNakePairInBox(allCandidates, boxRow, boxCol, allReason) { + for (let firstBoxIndex = 0; firstBoxIndex < BOX_SIZE*BOX_SIZE; firstBoxIndex ++) { + let firstRow = boxRow*BOX_SIZE + Math.floor(firstBoxIndex / BOX_SIZE); + let firstCol = boxCol*BOX_SIZE + firstBoxIndex % BOX_SIZE; + // 找到第一个二元候选值 + if (allCandidates[firstRow][firstCol].length == 2) { + for (let nextBoxIndex = firstBoxIndex + 1; nextBoxIndex !Array.isArray(element) || element.length !== 0); + allReason[firstRow][firstCol] = [...new Set(allReason[firstRow][firstCol])]; + allReason[nextRow][nextCol] = allReason[nextRow][nextCol].concat([`[${firstRow}, ${firstCol}] 和 [${nextRow}, ${nextCol}] 是一对宫Naked Pair, 候选值为${allCandidates[nextRow][nextCol]}`]).filter( + element => !Array.isArray(element) || element.length !== 0); + allReason[nextRow][nextCol] = [...new Set(allReason[nextRow][nextCol])]; + basicStragety.removeInBox(allCandidates, allReason, [[firstRow, firstCol], [nextRow, nextCol]], + [allCandidates[firstRow][firstCol][0], allCandidates[firstRow][firstCol][1]], boxRow, boxCol, this.description); + } + } + } + } + } +} + + +export const nakePairStrategy = new NakePairStrategy() + + + + +/* +// 调试信息 +let sudoku = [ + [0, 8, 0, 0, 9, 0, 0, 3, 0], + [0, 3, 0, 0, 0, 0, 0, 6, 9], + [9, 0, 2, 0, 6, 3, 1, 5, 8], + [0, 2, 0, 8, 0, 4, 5, 9, 0], + [8, 5, 1, 9, 0, 7, 0, 4, 6], + [3, 9, 4, 6, 0, 5, 8, 7, 0], + [5, 6, 3, 0, 4, 0, 9, 8, 7], + [2, 0, 0, 0, 0, 0, 0, 1, 5], + [0, 1, 0, 0, 5, 0, 0, 2, 0], +]; +let sudoku1 = [ + [4, 0, 0, 0, 0, 0, 9, 3, 8], + [0, 3, 2, 0, 9, 4, 1, 0, 0], + [0, 9, 5, 3, 0, 0, 2, 4, 0], + [3, 7, 0, 6, 0, 9, 0, 0, 4], + [5, 2, 9, 0, 0, 1, 6, 7, 3], + [6, 0, 4, 7, 0, 3, 0, 9, 0], + [9, 5, 7, 0, 0, 8, 3, 0, 0], + [0, 0, 3, 9, 0, 0, 4, 0, 0], + [2, 4, 0, 0, 3, 0, 7, 0, 9], +]; + +console.log(nakePairStrategy.apply(sudoku1)); +*/ diff --git a/src/node_modules/@sudoku/strategy/strategy.js b/src/node_modules/@sudoku/strategy/strategy.js new file mode 100644 index 00000000..7919a692 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/strategy.js @@ -0,0 +1,21 @@ + + +export class Strategy { + constructor(name, description) { + if (new.target === Strategy) { + throw new Error("Cannot instantiate abstract class Strategy directly"); + } + this.name = name; + this.description = description; + } + + apply(sudoku) { + throw new Error("Abstract method 'apply' must be implemented by a subclass"); + } + +} + + + + + diff --git a/src/node_modules/@sudoku/strategy/strategyManager.js b/src/node_modules/@sudoku/strategy/strategyManager.js new file mode 100644 index 00000000..532dd3c6 --- /dev/null +++ b/src/node_modules/@sudoku/strategy/strategyManager.js @@ -0,0 +1,170 @@ +import { Strategy } from './strategy.js'; +// import { BOX_SIZE, GRID_LENGTH, SUDOKU_SIZE, GRID_COORDS } from '@sudoku/constants'; +import {basicStragety} from '@sudoku/strategy/basicStrategy.js' +import { lastRemianStrategy } from '@sudoku/strategy/lastRemainStrategy.js'; +import { nakePairStrategy } from './nakePair.js' +import { hiddenPairStrategy } from './hiddenPair.js' +import { threeDMedusaStrategy } from './3DMedusaStrategy.js' + + +export class StrategyManager { + + constructor() { + this.strategyList = []; + this.candidateList = []; + this.reasonList = []; + } + + + register(strategy) { + if (strategy && strategy.apply) { + this.strategyList.push(strategy); + } else { + throw new Error('Invalid strategy: Strategy must have an apply method'); + } + } + + + removeStrategy(strategy) { + this.strategyList = this.strategyList.filter(s => s !== strategy); + } + + + inference(sudoku) { + this.candidateList = []; + for (const strategy of this.strategyList) { + const [newCandidates, newReason] = strategy.apply(sudoku); + this.candidateList.push(newCandidates); + this.reasonList.push(newReason); + } + return this.intersection(); + } + + + + // 计算所有策略的结果取交集 + intersection() { + if (this.candidateList.length === 0) { + return []; + } + // 初始化交集为第一个策略的结果 + let intersectionResult = this.candidateList[0]; + let intersectionReason = this.reasonList[0]; + // 逐个计算交集 + for (let i = 1; i < this.candidateList.length; i++) { + const currentCandidates = this.candidateList[i]; + const currentReanson = this.reasonList[i]; + for (let row = 0; row < intersectionResult.length; row++) { + for (let col = 0; col < intersectionResult[row].length; col++) { + // 候选值结果取交集 + intersectionResult[row][col] = intersectionResult[row][col].filter(element => currentCandidates[row][col].includes(element)); + // 理由取并集 + intersectionReason[row][col] = intersectionReason[row][col].concat(currentReanson[row][col]); + intersectionReason[row][col] = intersectionReason[row][col].filter(element => !Array.isArray(element) || element.length !== 0); + } + } + } + // 返回交集结果 + // 去掉重复元素 + for(let row = 0; row < intersectionReason.length; row ++){ + for (let col = 0; col < intersectionReason[0].length; col ++) { + intersectionReason[row][col] = [...new Set(intersectionReason[row][col])] + } + } + return [intersectionResult, intersectionReason]; + } + +} + + + + + + + + +// 调试代码 +export const strategymanager = new StrategyManager(); + + +// 注册策略 +strategymanager.register(basicStragety) +strategymanager.register(lastRemianStrategy) +strategymanager.register(nakePairStrategy); +strategymanager.register(hiddenPairStrategy); +strategymanager.register(threeDMedusaStrategy); + + +/* +let sudoku = [ + [6, 0, 0, 9, 0, 8, 7, 0, 3], + [2, 0, 7, 0, 4, 0, 0, 0, 0], + [0, 5, 0, 0, 0, 0, 6, 0, 0], + [0, 0, 3, 0, 0, 1, 0, 5, 0], + [0, 0, 2, 0, 8, 0, 0, 9, 0], + [1, 4, 0, 0, 3, 0, 0, 0, 2], + [0, 0, 0, 0, 0, 0, 2, 0, 0], + [0, 8, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 7], +]; + + +let sudoku = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [5, 0, 9, 8, 0, 0, 0, 1, 0], + [0, 7, 3, 0, 5, 0, 0, 9, 0], + [2, 0, 8, 0, 0, 0, 7, 0, 0], + [0, 0, 0, 6, 8, 0, 0, 5, 0], + [0, 0, 0, 0, 2, 0, 0, 0, 3], + [8, 0, 0, 0, 0, 5, 0, 0, 1], + [7, 0, 0, 0, 0, 3, 0, 6, 0], + [0, 0, 0, 1, 9, 0, 3, 0, 0], +] + +let sudoku = [ + [7, 2, 0, 4, 0, 8, 0, 3, 0], + [0, 8, 0, 0, 0, 0, 0, 4, 7], + [4, 0, 1, 0, 7, 6, 8, 0, 2], + [8, 1, 0, 7, 3, 9, 0, 0, 0], + [0, 0, 0, 8, 5, 1, 0, 0, 0], + [0, 0, 0, 2, 6, 4, 0, 8, 0], + [2, 0, 9, 6, 8, 0, 4, 1, 3], + [3, 4, 0, 0, 0, 0, 0, 0, 8], + [1, 6, 8, 9, 4, 3, 2, 7, 5], +] + +let sudoku = [ + [6, 0, 0, 0, 1, 3, 8, 9, 0], + [0, 0, 0, 0, 7, 0, 6, 4, 0], + [0, 0, 0, 0, 4, 6, 3, 7, 1], + [0, 0, 0, 0, 0, 0, 2, 0, 4], + [5, 0, 0, 0, 0, 0, 1, 0, 6], + [0, 8, 0, 0, 0, 0, 7, 0, 9], + [0, 0, 5, 3, 6, 2, 9, 1, 8], + [0, 9, 0, 4, 8, 1, 5, 0, 7], + [8, 0, 1, 7, 5, 9, 4, 0, 3], +] + + +let sudoku = [ + [0, 9, 3, 8, 2, 4, 5, 6, 0], + [0, 8, 5, 6, 0, 0, 0, 0, 2], + [2, 0, 6, 0, 7, 5, 0, 0, 8], + [3, 2, 1, 7, 6, 9, 8, 4, 5], + [0, 0, 0, 2, 5, 8, 3, 0, 0], + [5, 7, 8, 0, 4, 0, 2, 9, 6], + [8, 5, 0, 0, 1, 6, 7, 2, 3], + [0, 0, 7, 0, 8, 2, 6, 5, 0], + [0, 0, 2, 5, 0, 7, 1, 8, 0], +]; + +// 应用所有策略 +const [allCandidates, allReason] = strategymanager.inference(sudoku); +console.log(allCandidates) +console.log(allReason) +*/ + + + + + diff --git a/src/template.html b/src/template.html index f31623e3..fbb2f7e0 100644 --- a/src/template.html +++ b/src/template.html @@ -25,3 +25,8 @@ + + \ No newline at end of file diff --git a/src/test/strategyManager.test.js b/src/test/strategyManager.test.js new file mode 100644 index 00000000..d3bb563b --- /dev/null +++ b/src/test/strategyManager.test.js @@ -0,0 +1,154 @@ +// strategyManager.test.js + +import { StrategyManager } from './node_modules/@sudoku/strategy/strategyManager.js'; +import { lastRemainStrategy } from './node_modules/@sudoku/strategy/lastRemainStrategy.js'; +import { nakePairStrategy } from './node_modules/@sudoku/strategy/nakePair.js'; +import { hiddenPairStrategy } from './node_modules/@sudoku/strategy/hiddenPair.js'; + +const testCases = [ + { + name: 'Very Easy Sudoku', + puzzle: [ + [5, 3, 0, 0, 7, 0, 0, 0, 0], + [6, 0, 0, 1, 9, 5, 0, 0, 0], + [0, 9, 8, 0, 0, 0, 0, 6, 0], + [8, 0, 0, 0, 6, 0, 0, 0, 3], + [4, 0, 0, 8, 0, 3, 0, 0, 1], + [7, 0, 0, 0, 2, 0, 0, 0, 6], + [0, 6, 0, 0, 0, 0, 2, 8, 0], + [0, 0, 0, 4, 1, 9, 0, 0, 5], + [0, 0, 0, 0, 8, 0, 0, 7, 9] + ], + solution: [ + [5, 3, 4, 6, 7, 8, 9, 1, 2], + [6, 7, 2, 1, 9, 5, 3, 4, 8], + [1, 9, 8, 3, 4, 2, 5, 6, 7], + [8, 5, 9, 7, 6, 1, 4, 2, 3], + [4, 2, 6, 8, 5, 3, 7, 9, 1], + [7, 1, 3, 9, 2, 4, 8, 5, 6], + [9, 6, 1, 5, 3, 7, 2, 8, 4], + [2, 8, 7, 4, 1, 9, 6, 3, 5], + [3, 4, 5, 2, 8, 6, 1, 7, 9] + ] + }, + { + name: 'Easy Sudoku', + puzzle: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 0, 8, 5], + [0, 0, 1, 0, 2, 0, 0, 0, 0], + [0, 0, 0, 5, 0, 7, 0, 0, 0], + [0, 0, 4, 0, 0, 0, 1, 0, 0], + [0, 9, 0, 0, 0, 0, 0, 0, 0], + [5, 0, 0, 0, 0, 0, 0, 7, 3], + [0, 0, 2, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 4, 0, 0, 0, 9] + ], + solution: [ + [1, 4, 5, 3, 2, 9, 6, 7, 8], + [6, 7, 2, 1, 5, 3, 4, 8, 9], + [8, 3, 9, 6, 7, 4, 2, 5, 1], + [1, 2, 6, 5, 3, 7, 9, 4, 8], + [3, 5, 4, 9, 8, 6, 1, 2, 7], + [9, 8, 7, 2, 1, 5, 3, 6, 4], + [5, 1, 8, 4, 9, 2, 7, 3, 6], + [4, 9, 2, 8, 6, 1, 5, 3, 7], + [7, 6, 3, 5, 4, 8, 9, 1, 2] + ] + }, + { + name: 'Hard Sudoku', + puzzle: [ + [8, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 3, 6, 0, 0, 0, 0, 0], + [0, 7, 0, 0, 9, 0, 2, 0, 0], + [0, 5, 0, 0, 0, 7, 0, 0, 0], + [0, 0, 0, 0, 4, 5, 7, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 3, 0], + [0, 0, 1, 0, 0, 0, 0, 6, 8], + [0, 0, 8, 5, 0, 0, 0, 1, 0], + [0, 9, 0, 0, 0, 0, 4, 0, 0] + ], + solution: [ + [8, 1, 2, 7, 5, 3, 6, 4, 9], + [9, 4, 3, 6, 8, 2, 1, 7, 5], + [6, 7, 5, 4, 9, 1, 2, 8, 3], + [1, 5, 4, 2, 3, 7, 8, 9, 6], + [3, 6, 9, 8, 4, 5, 7, 2, 1], + [2, 8, 7, 1, 6, 9, 5, 3, 4], + [5, 2, 1, 9, 7, 4, 3, 6, 8], + [4, 3, 8, 5, 2, 6, 9, 1, 7], + [7, 9, 6, 3, 1, 8, 4, 5, 2] + ] + }, + { + name: 'Very Hard Sudoku', + puzzle: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 0, 8, 0], + [0, 0, 1, 0, 2, 0, 0, 0, 0], + [0, 0, 0, 5, 0, 7, 0, 0, 0], + [0, 0, 4, 0, 0, 0, 1, 0, 0], + [0, 9, 0, 0, 0, 0, 0, 0, 0], + [5, 0, 0, 0, 0, 0, 0, 7, 3], + [0, 0, 2, 0, 1, 0, 9, 0, 0], + [0, 0, 0, 0, 4, 0, 0, 0, 0] + ], + solution: [ + [4, 3, 5, 2, 6, 9, 7, 8, 1], + [6, 8, 2, 5, 7, 1, 4, 3, 9], + [1, 9, 7, 8, 3, 4, 5, 6, 2], + [8, 2, 6, 1, 9, 5, 3, 4, 7], + [3, 7, 4, 6, 8, 2, 9, 1, 5], + [9, 5, 1, 7, 4, 3, 6, 2, 8], + [5, 1, 9, 3, 2, 6, 8, 7, 4], + [2, 4, 8, 9, 5, 7, 1, 3, 6], + [7, 6, 3, 4, 1, 8, 2, 9, 5] + ] + } +]; + +describe('StrategyManager', () => { + let strategyManager; + + beforeEach(() => { + strategyManager = new StrategyManager(); + strategyManager.register(lastRemainStrategy); + strategyManager.register(nakedPairStrategy); + strategyManager.register(hiddenPairStrategy); + }); + + testCases.forEach(testCase => { + test(`should correctly infer candidates for ${testCase.name}`, () => { + const { puzzle, solution } = testCase; + const [intersectionResult, intersectionReason] = strategyManager.inference(puzzle); + + // Check if intersectionResult provides valid candidates for each blank cell + for (let row = 0; row < 9; row++) { + for (let col = 0; col < 9; col++) { + if (puzzle[row][col] === 0) { // Check only blank cells + expect(intersectionResult[row][col]).toBeInstanceOf(Array); + expect(intersectionResult[row][col].length).toBeGreaterThan(0); // Expect at least one candidate + expect(intersectionResult[row][col]).not.toContain(puzzle[row][col]); // Should not contain the number already in the cell + expect(intersectionResult[row][col]).toContain(solution[row][col]); // Should contain the correct solution number as a candidate + } + } + } + + // Check if intersectionReason provides reasons for each blank cell + for (let row = 0; row < 9; row++) { + for (let col = 0; col < 9; col++) { + if (puzzle[row][col] === 0) { // Check only blank cells + expect(intersectionReason[row][col]).toBeInstanceOf(Array); + expect(intersectionReason[row][col].length).toBeGreaterThan(0); // Expect at least one reason + } + } + } + }); + }); + + test('should throw error when registering invalid strategy', () => { + const invalidStrategy = {}; + expect(() => strategyManager.register(invalidStrategy)).toThrow('Invalid strategy: Strategy must have an apply method'); + }); +}); \ No newline at end of file diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103338-u8xdgl5.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103338-u8xdgl5.png" new file mode 100644 index 00000000..0f816b38 Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103338-u8xdgl5.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103355-5toit68.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103355-5toit68.png" new file mode 100644 index 00000000..d45a05bc Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113103355-5toit68.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205635-9x4yjp3.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205635-9x4yjp3.png" new file mode 100644 index 00000000..d379a1ad Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205635-9x4yjp3.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205700-rzgb3ws.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205700-rzgb3ws.png" new file mode 100644 index 00000000..dcb55dfe Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250113205700-rzgb3ws.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232659-gas63vo.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232659-gas63vo.png" new file mode 100644 index 00000000..53749840 Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232659-gas63vo.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232713-aeogkm1.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232713-aeogkm1.png" new file mode 100644 index 00000000..dc37d256 Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250116232713-aeogkm1.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104426-5b3isah.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104426-5b3isah.png" new file mode 100644 index 00000000..4e3ecfb5 Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104426-5b3isah.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104447-rz2y2r5.png" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104447-rz2y2r5.png" new file mode 100644 index 00000000..e49e8450 Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/assets/image-20250117104447-rz2y2r5.png" differ diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.md" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.md" new file mode 100644 index 00000000..29498353 --- /dev/null +++ "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.md" @@ -0,0 +1,754 @@ +# 下一步提示实现具体说明 + +## 提示交互逻辑的修改 + +### 1. 设置项 + +在设置中加入 `hintLevel`​ 选项,备选值为 {1,2,3},通过 `hintLevel`​ 指明通过提示按钮获得的提示等级,`hintLevel`​ 值可以通过设置按钮来修改。 + +修改涉及以下文件: + +1. src/node\_modules/@sudoku/constants.js +2. src/components/Modal/Types/Settings.svelte + +实现效果如下: + +​![image](assets/image-20250113103338-u8xdgl5.png)​ + +​![image](assets/image-20250113103355-5toit68.png)​ + +### 2. 提示的应用逻辑和显示效果 + +原项目的提示交互逻辑: + +1. 前端展示:在操作栏中展示提示按钮,并根据提示次数的可用性控制按钮的状态。 +2. 用户交互:用户点击提示按钮后,前端调用 handleHint 函数,触发提示逻辑。 +3. 状态管理:hints store 管理提示次数,userGrid store 管理数独棋盘的状态。 +4. 提示逻辑:调用 solveSudoku 方法求解数独,并将当前单元格的值设置为求解后的正确值。 +5. 后端求解:使用 @mattflow/sudoku-solver 库进行数独求解,返回正确的数字。 + +将其修改为: + +1. 用户点击提示按钮后,调用 handleHint 触发提示逻辑。 +2. 求解数独,在当前单元格上显示求解后正确的值,但并不修改 userGrid 的状态。 +3. 如果用户点击当前单元格,如果单元格上只有唯一候选值,则将当前单元格上的值填入 userGrid 中;如果单元格上有多个提示候选值,则不填入,由用户自己在键盘上输入数值填入。 +4. 如果用户点击其他单元格,则不再显示当前单元格上的提示内容。 + +涉及修改的文件: + +1. src/components/Board/Cell.svelte +2. src/components/Board/index.svelte +3. src/node_modules/@sudoku/stores/hintStore.js → 该项为新增 +4. src/node_modules/@sudoku/stores/hints.js +5. src/node_modules/@sudoku/stores/grid.js + +实现效果如下: + +​![image](assets/image-20250113205635-9x4yjp3.png "点击提示")​ + +​![image](assets/image-20250113205700-rzgb3ws.png "点击单元格")​ + +具体交互逻辑的说明: + +1. 用户点击提示按钮,触发 Hints 类的 useHint 方法,通过和策略类交互,将当前棋盘状态送入策略类,策略类返回一个两个数组:第一个数组是通过调用策略得到的棋盘候选值,可以理解为一个 9×9 的二维数组,数组的元素也是数组:表示每个位置上的候选值列表,该列表长度就是候选值的数量;第二个数组是策略类返回的提示原因,也是一个 9×9 的二维数组,数组的元素是一个说明原因的列表。 +2. Hints 类通过筛选得到的候选值列表的长度,将其分组到三个列表中:长度为1(仅一个候选值)的进 level_one_list,长度为2的进 level_two_list,长度<9的进 level_three_list,得到三个列表后根据设置中的 hintLevel 选项决定将哪个列表传递到 userGrid 类。 +3. userGrid 类将 Hints 类传递过来的解数组存入 hintStore 中,hintStore 通过一个 writable 的 store 存储解数组,在前端 Cell.svelte 中对 hintStore 进行订阅。 +4. 点击提示后,hintStore 中的状态发生改变,Cell.svelte 的前端展示部分会把新增的提示内容显示在对应的单元格上,如果点击提示,则将提示值填入 userGrid,同时清空 hintStore 的存储状态;如果点击其他没有提示的单元格,同样清空 hintStore 的存储状态。 +5. 同时 Hints 类中得到的原因数组也会通过一个 writable 的 store 进行存储,在 Cell.svelte 中导入,如果点击提示的单元格上有相应的原因,则会在棋盘左侧显示原因的文字。 + +### 3. 与策略类的对接 + +解题策略相关的内容在 sudoku-main\src\node_modules\@sudoku\strategy 目录下。用法: + +* strategyManager.js 是策略的管理类。 +* 具体的导出类实例、注册策略: + + ```javascript + export const strategymanager = new StrategyManager(); + + // 注册策略 + strategymanager.register(lastRemianStrategy) + strategymanager.register(nakePairStrategy); + strategymanager.register(hiddenPairStrategy); + ``` + +实现效果如下: + +​![image](assets/image-20250116232659-gas63vo.png "一级提示")​ + +​![image](assets/image-20250116232713-aeogkm1.png "提示后弹出原因")​ + +​![image](assets/image-20250117104426-5b3isah.png "二级提示")​ + +​![image](assets/image-20250117104447-rz2y2r5.png "三级提示")​ + +## 其他说明 + +1. 代码中的 console.log 均为调试语句,可以删除。 +2. 如果提示只有一个候选值,点击单元格会填入提示值;如果提示有多个候选值,点击单元格则提示内容消失,单元格的值由用户自行填入。 + +## 具体修改的代码(整个粘贴) + +### src/node_modules/@sudoku/constants.js + +```svelte +export const BASE_URL = 'https://sudoku.jonasgeiler.com/'; + +export const DIFFICULTY_CUSTOM = 'custom'; +export const DIFFICULTIES = { + veryeasy: 'Very Easy', + easy: 'Easy', + medium: 'Medium', + hard: 'Hard', +}; + +export const DEFAULT_SETTINGS = { + darkTheme: false, + displayTimer: true, + hintsLimited: true, + hints: 5, + highlightCells: true, + highlightSame: true, + highlightConflicting: true, + hintLevel: 1, // 新增 hintLevel,默认值为 1 +}; +export const MAX_HINTS = 99999; + +export const SUDOKU_SIZE = 9; +export const BOX_SIZE = 3; +export const GRID_LENGTH = SUDOKU_SIZE * SUDOKU_SIZE; +export const GRID_COORDS = [[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[1,0],[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7],[1,8],[2,0],[2,1],[2,2],[2,3],[2,4],[2,5],[2,6],[2,7],[2,8],[3,0],[3,1],[3,2],[3,3],[3,4],[3,5],[3,6],[3,7],[3,8],[4,0],[4,1],[4,2],[4,3],[4,4],[4,5],[4,6],[4,7],[4,8],[5,0],[5,1],[5,2],[5,3],[5,4],[5,5],[5,6],[5,7],[5,8],[6,0],[6,1],[6,2],[6,3],[6,4],[6,5],[6,6],[6,7],[6,8],[7,0],[7,1],[7,2],[7,3],[7,4],[7,5],[7,6],[7,7],[7,8],[8,0],[8,1],[8,2],[8,3],[8,4],[8,5],[8,6],[8,7],[8,8]]; +export const CANDIDATE_COORDS = [[1, 1],[1, 2],[1, 3],[2, 1],[2, 2],[2, 3],[3, 1],[3, 2],[3, 3]]; + +export const SENCODE_SEPARATOR = '-'; +export const SENCODE_SEPARATOR_REVERSE = '_'; +export const SENCODE_REGEX = new RegExp('^[a-zA-Z0-9]+[' + SENCODE_SEPARATOR + SENCODE_SEPARATOR_REVERSE + '][a-zA-Z0-9]+$'); + +export const BASE62_CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + +export const MODAL_NONE = 'none'; // Modal type when hidden +export const MODAL_DURATION = 400; + +export const DROPDOWN_DURATION = MODAL_DURATION; + +export const GAME_OVER_CELEBRATIONS = [ + 'Excellent!', + 'Wow!', + 'Congratulations!', + 'Oh yeah!', + 'Impressive!', + 'Good work!', + 'You did great!', + 'Applause!', + 'Great!' +] +``` + +### src/components/Modal/Types/Settings.svelte + +```javascript + + +

Settings

+ + + +
+ + + +
+ + +
+ + + + + + {#if hintsLimited} +
+ + + +
+ {/if} + + + + +
+ +
+ + +
+ + +``` + +### src/components/Board/Cell.svelte + +```svelte + + +
+ + {#if !disabled} +
+ + + + +
+ {/if} +
+ + + + +``` + +### src/components/Board/index.svelte + +```svelte + + +
+ + +
+ {$hintText} +
+ +
+
+
+
+ +
+ + {#each $userGrid as row, y} + {#each row as value, x} + + {/each} + {/each} + +
+ +
+
+ + +``` + +### store/hints.js + +```javascript +import { writable } from 'svelte/store'; +import { settings } from './settings'; +import { solveSudoku } from '@sudoku/sudoku'; + +export const usedHints = writable(0); + +// 新增:用于存储提示文字的状态 +export const hintText = writable(''); + +class Hints { + #hints; + #defaultHints; + #hintLevel; + + constructor() { + this.#defaultHints = Infinity; + this.#hints = writable(Infinity); + + settings.subscribe(($settings) => { + this.#hintLevel = $settings.hintLevel; + if ($settings.hintsLimited) { + this.#defaultHints = $settings.hints; + this.#hints.update(($hints) => { + if ($hints > $settings.hints) return $settings.hints; + return $hints; + }); + } else { + this.#defaultHints = Infinity; + this.#hints.set(Infinity); + } + }); + } + + subscribe(callback) { + return this.#hints.subscribe(callback); + } + + /** + * 使用提示功能,返回提示内容。 + * @param {Array>} currentGrid 当前用户数独网格 + * @param {Object} pos 提示的坐标 { x, y } + * @returns {number|null} 提示的值,若无可用提示则返回 null + */ + useHint(currentGrid, pos) { + let hintValue = []; + this.#hints.update(($hints) => { + if ($hints > 0) { + usedHints.update(($usedHints) => $usedHints + 1); + + if (this.#hintLevel === 1) { //一级提示:提示所有仅剩一个候选值的单元格 + const solvedSudoku = solveSudoku(currentGrid); + if (solvedSudoku && solvedSudoku[pos.x] !== undefined) { + hintValue.push(solvedSudoku[pos.x][pos.y]); + } + hintText.set('Level 1 Hint: 测试'); + } else if (this.#hintLevel === 2) { //二级提示:提示所有剩两个候选值的单元格 + const solvedSudoku = solveSudoku(currentGrid); + if (solvedSudoku && solvedSudoku[pos.x] !== undefined) { + hintValue.push(solvedSudoku[pos.x][pos.y]); + } + hintText.set('Level 2 Hint: 测试'); + } else { //三级提示:提示所有候选值 < 9 的单元格 + const solvedSudoku = solveSudoku(currentGrid); + if (solvedSudoku && solvedSudoku[pos.x] !== undefined) { + hintValue.push(solvedSudoku[pos.x][pos.y]); + } + hintText.set('Level 3 Hint: 测试'); + } + + return $hints - 1; + } + return $hints; // 提示次数为 0,不进行更新 + }); + + return hintValue; + } + + reset() { + this.#hints.set(this.#defaultHints); + usedHints.set(0); + } +} + +export const hints = new Hints(); + +``` + +### store/grid.js + +```javascript +import { BOX_SIZE, SUDOKU_SIZE } from '@sudoku/constants'; +import { decodeSencode, encodeSudoku } from '@sudoku/sencode'; +import { generateSudoku } from '@sudoku/sudoku'; +import { derived, writable } from 'svelte/store'; +import { hintStore } from '@sudoku/stores/hintStore'; +import { hints } from './hints'; +import { get } from 'svelte/store'; + + +class Grid { + #grid; + + constructor() { + this.#grid = writable( + Array(SUDOKU_SIZE).fill().map(() => Array(SUDOKU_SIZE).fill(0)) + ); + } + + subscribe(callback) { + return this.#grid.subscribe(callback); + } + + generate(difficulty) { + this.#grid.set(generateSudoku(difficulty)); + } + + decodeSencode(sencode) { + this.#grid.set(decodeSencode(sencode)); + } + + get(gridStore, x, y) { + return gridStore[y][x]; + } + + getSencode(gridStore) { + return encodeSudoku(gridStore); + } +} + +export const grid = new Grid(); + +class UserGrid { + #userGrid; + + constructor(gridInstance) { + this.#userGrid = writable( + Array(SUDOKU_SIZE).fill().map(() => Array(SUDOKU_SIZE).fill(0)) + ); + + gridInstance.subscribe($grid => { + const newGrid = $grid.map(row => [...row]); + this.#userGrid.set(newGrid); + }); + } + + subscribe(callback) { + return this.#userGrid.subscribe(callback); + } + + set(pos, value) { + this.#userGrid.update($userGrid => { + $userGrid[pos.y][pos.x] = value; + return $userGrid; + }); + } + + /** + * 与 hints.js 交互,获取提示值并保存到 hintStore 中。 + * @param {Object} pos 提示的坐标 { x, y } + */ + applyHint(pos) { + + const currentUserGrid = get(this.#userGrid); + + let solve = hints.useHint(currentUserGrid); + console.log("Solve:",solve); + + if (solve instanceof Array) { + hintStore.hint_array_set(solve); + } + + } +} + +export const userGrid = new UserGrid(grid); + +export const invalidCells = derived(userGrid, $userGrid => { + const _invalidCells = []; + + const addInvalid = (x, y) => { + const xy = `${x},${y}`; + if (!_invalidCells.includes(xy)) _invalidCells.push(xy); + }; + + for (let y = 0; y < SUDOKU_SIZE; y++) { + for (let x = 0; x < SUDOKU_SIZE; x++) { + const value = $userGrid[y][x]; + + if (value) { + for (let i = 0; i < SUDOKU_SIZE; i++) { + // Check the row + if (i !== x && $userGrid[y][i] === value) { + addInvalid(x, y); + } + + // Check the column + if (i !== y && $userGrid[i][x] === value) { + addInvalid(x, i); + } + } + + // Check the box + const startY = Math.floor(y / BOX_SIZE) * BOX_SIZE; + const endY = startY + BOX_SIZE; + const startX = Math.floor(x / BOX_SIZE) * BOX_SIZE; + const endX = startX + BOX_SIZE; + for (let row = startY; row < endY; row++) { + for (let col = startX; col < endX; col++) { + if (row !== y && col !== x && $userGrid[row][col] === value) { + addInvalid(col, row); + } + } + } + } + } + } + + return _invalidCells; +}, []); +``` diff --git "a/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.pdf" "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.pdf" new file mode 100644 index 00000000..e1487ffe Binary files /dev/null and "b/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\350\257\264\346\230\216\346\226\207\346\241\243/\344\270\213\344\270\200\346\255\245\346\217\220\347\244\272\345\256\236\347\216\260\345\205\267\344\275\223\350\257\264\346\230\216.pdf" differ diff --git "a/\346\212\245\345\221\212.docx" "b/\346\212\245\345\221\212.docx" new file mode 100644 index 00000000..533c528c Binary files /dev/null and "b/\346\212\245\345\221\212.docx" differ diff --git "a/\346\212\245\345\221\212.pdf" "b/\346\212\245\345\221\212.pdf" new file mode 100644 index 00000000..aa6e6f96 Binary files /dev/null and "b/\346\212\245\345\221\212.pdf" differ diff --git "a/\346\274\224\347\244\272\350\247\206\351\242\2211.zip" "b/\346\274\224\347\244\272\350\247\206\351\242\2211.zip" new file mode 100644 index 00000000..10b3aad5 Binary files /dev/null and "b/\346\274\224\347\244\272\350\247\206\351\242\2211.zip" differ diff --git "a/\346\274\224\347\244\272\350\247\206\351\242\2212_\345\233\236\346\272\257\345\212\237\350\203\275\346\274\224\347\244\272.mp4" "b/\346\274\224\347\244\272\350\247\206\351\242\2212_\345\233\236\346\272\257\345\212\237\350\203\275\346\274\224\347\244\272.mp4" new file mode 100644 index 00000000..805a9b7f Binary files /dev/null and "b/\346\274\224\347\244\272\350\247\206\351\242\2212_\345\233\236\346\272\257\345\212\237\350\203\275\346\274\224\347\244\272.mp4" differ