diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml new file mode 100644 index 0000000..e214ed9 --- /dev/null +++ b/.github/workflows/js.yml @@ -0,0 +1,26 @@ +name: Javascript + +on: + push: + branches: + - master + paths: + - 'js/**' + - '.github/workflows/js.yml' + pull_request: + paths: + - 'js/**' + - '.github/workflows/js.yml' + workflow_dispatch: + +jobs: + common: + name: Common + uses: ./.github/workflows/common.yml + with: + workdir: js + format-pkgs: nodejs libsdl2-dev + build-pkgs: nodejs libsdl2-dev libc-bin + cache-paths: | + js/node_modules + cache-file: '**/package-lock.json' diff --git a/README.md b/README.md index 0902ca4..ba653dc 100644 --- a/README.md +++ b/README.md @@ -86,26 +86,24 @@ each compiler, with standard "release mode" flags (see each language's ``` $ ./all.py bench | ./utils/sort.py - nim / lto : Emulated 16438 frames in 10.00s (1643fps) - rs / lto : Emulated 16029 frames in 10.00s (1603fps) - cpp / lto : Emulated 15064 frames in 10.00s (1506fps) - c / lto : Emulated 15054 frames in 10.00s (1505fps) - rs / release: Emulated 13904 frames in 10.00s (1390fps) - c / release: Emulated 13368 frames in 10.00s (1337fps) - cpp / release: Emulated 12967 frames in 10.00s (1297fps) - nim / release: Emulated 11860 frames in 10.00s (1185fps) - zig / release: Emulated 8844 frames in 10.00s (884fps) - zig / safe : Emulated 7206 frames in 10.00s (721fps) - c / debug : Emulated 5914 frames in 10.00s (591fps) - cpp / debug : Emulated 5663 frames in 10.00s (566fps) - go / release: Emulated 5029 frames in 10.00s (503fps) - pxd / release: Emulated 3610 frames in 10.00s (361fps) - nim / debug : Emulated 2587 frames in 10.00s (258fps) - rs / debug : Emulated 1808 frames in 10.01s (181fps) - py / mypyc : Emulated 789 frames in 10.01s (79fps) - php / opcache: Emulated 655 frames in 10.00s (65fps) - php / release: Emulated 257 frames in 10.02s (26fps) - py / release: Emulated 165 frames in 10.02s (16fps) + rs / lto : Emulated 15763 frames in 10.00s (1576fps) + cpp / lto : Emulated 14737 frames in 10.00s (1474fps) + rs / release: Emulated 13183 frames in 10.00s (1318fps) + cpp / release: Emulated 12966 frames in 10.00s (1297fps) + zig / release: Emulated 8792 frames in 10.00s (879fps) + nim / speed : Emulated 8127 frames in 10.00s (812fps) + nim / release: Emulated 6161 frames in 10.00s (616fps) + cpp / debug : Emulated 5693 frames in 10.00s (569fps) + go / release: Emulated 5040 frames in 10.00s (504fps) + pxd / release: Emulated 3792 frames in 10.00s (379fps) + js / release: Emulated 2436 frames in 10.00s (243fps) # missing GPU + nim / debug : Emulated 1968 frames in 10.00s (196fps) + rs / debug : Emulated 1676 frames in 10.00s (168fps) + py / mypyc : Emulated 887 frames in 10.01s (89fps) + php / opcache: Emulated 613 frames in 10.01s (61fps) + php / release: Emulated 255 frames in 10.01s (25fps) + py / release: Emulated 101 frames in 10.06s (10fps) + zig / safe : Emulated 40 frames in 10.00s (4fps) ``` Also if you spot some bit of code that is weirdly slow and making your favourite diff --git a/js/.gitignore b/js/.gitignore new file mode 100644 index 0000000..6d34cdf --- /dev/null +++ b/js/.gitignore @@ -0,0 +1,3 @@ +node_modules +.parcel-cache +dist diff --git a/js/README.md b/js/README.md new file mode 100644 index 0000000..7603078 --- /dev/null +++ b/js/README.md @@ -0,0 +1,19 @@ +RosettaBoy - JavaScript (TypeScript (Node)) Version +=================================================== + +Thoughts on JS / TS / Node +-------------------------- + +On TypeScript vs JavaScript: +* TypeScript seems better in every way, except that it requires a build step :( + +On the state of NPM libraries: +* Try one SDL library, it doesn't compile +* Try another SDL library, realise it hasn't been updated in 8 years and doesn't work with current Node +* Try a third SDL library, realise that it makes a lot of assumptions and has its own API that's very different from SDL +* Find some modern, supported, well-behaved libraries... which _exclusively_ cover the audio interface and nothing else +* Give up on SDL libraries, try the FFI library instead to build my own +* Find that there's no official FFI library, so I try the FFI library on NPM +* Find that the FFI library on NPM was abandoned several years ago +* Seems like somebody forked the above to make a stable version and _has_ kept it up to date, so let's try that... +* The up-to-date FFI library links to the abandoned library's docs \ No newline at end of file diff --git a/js/build.sh b/js/build.sh new file mode 100755 index 0000000..5d339a9 --- /dev/null +++ b/js/build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname $0) +if [ ! -d node_modules ] ; then + npm install +fi +npm run build + +cat >rosettaboy-release <=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@lezer/common": { + "version": "0.15.12", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", + "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==", + "dev": true + }, + "node_modules/@lezer/lr": { + "version": "0.15.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz", + "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==", + "dev": true, + "dependencies": { + "@lezer/common": "^0.15.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz", + "integrity": "sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz", + "integrity": "sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz", + "integrity": "sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz", + "integrity": "sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz", + "integrity": "sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz", + "integrity": "sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mischnic/json-sourcemap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz", + "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==", + "dev": true, + "dependencies": { + "@lezer/common": "^0.15.7", + "@lezer/lr": "^0.15.4", + "json5": "^2.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@parcel/bundler-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.8.0.tgz", + "integrity": "sha512-OvDDhxX4LwfGe7lYVMbJMzqNcDk8ydOqNw0Hra9WPgl0m5gju/eVIbDvot3JXp5F96FmV36uCxdODJhKTNoAzQ==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/graph": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/cache": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.8.0.tgz", + "integrity": "sha512-k945hrafMDR2wyCKyZYgwypeLLuZWce6FzhgunI4taBUeVnNCcpFAWzbfOVQ39SqZTGDqG3MNT+VuehssHXxyg==", + "dev": true, + "dependencies": { + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/utils": "2.8.0", + "lmdb": "2.5.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/codeframe": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.8.0.tgz", + "integrity": "sha512-821d+KVcpEvJNMj9WMC39xXZK6zvRS/HUjQag2f3DkcRcZwk1uXJZdW6p1EB7C3e4e/0KSK3NTSVGEvbOSR+9w==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/compressor-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.8.0.tgz", + "integrity": "sha512-tM49t0gDQnwJbrDCeoCn9LRc8inZ/TSPQTttJTfcmFHHFqEllI0ZDVG0AiQw5NOMQbBLYiKun1adXn8pkcPLEA==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/config-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.8.0.tgz", + "integrity": "sha512-j9g50QNSLjuNpY0TP01EgGJPxWNes9d+e8+N07Z5Wv0u+UUnJ2uIOpo7PVn7ullOGhm1f9lP4KsJenu5gWb+cg==", + "dev": true, + "dependencies": { + "@parcel/bundler-default": "2.8.0", + "@parcel/compressor-raw": "2.8.0", + "@parcel/namer-default": "2.8.0", + "@parcel/optimizer-css": "2.8.0", + "@parcel/optimizer-htmlnano": "2.8.0", + "@parcel/optimizer-image": "2.8.0", + "@parcel/optimizer-svgo": "2.8.0", + "@parcel/optimizer-terser": "2.8.0", + "@parcel/packager-css": "2.8.0", + "@parcel/packager-html": "2.8.0", + "@parcel/packager-js": "2.8.0", + "@parcel/packager-raw": "2.8.0", + "@parcel/packager-svg": "2.8.0", + "@parcel/reporter-dev-server": "2.8.0", + "@parcel/resolver-default": "2.8.0", + "@parcel/runtime-browser-hmr": "2.8.0", + "@parcel/runtime-js": "2.8.0", + "@parcel/runtime-react-refresh": "2.8.0", + "@parcel/runtime-service-worker": "2.8.0", + "@parcel/transformer-babel": "2.8.0", + "@parcel/transformer-css": "2.8.0", + "@parcel/transformer-html": "2.8.0", + "@parcel/transformer-image": "2.8.0", + "@parcel/transformer-js": "2.8.0", + "@parcel/transformer-json": "2.8.0", + "@parcel/transformer-postcss": "2.8.0", + "@parcel/transformer-posthtml": "2.8.0", + "@parcel/transformer-raw": "2.8.0", + "@parcel/transformer-react-refresh-wrap": "2.8.0", + "@parcel/transformer-svg": "2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.8.0.tgz", + "integrity": "sha512-udzbe3jjbpfKlRE9pdlROAa+lvAjS1L/AzN6r2j1y/Fsn7ze/NfvnCFw6o2YNIrXg002aQ7M1St/x1fdGfmVKA==", + "dev": true, + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/cache": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/graph": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "abortcontroller-polyfill": "^1.1.9", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^7.0.0", + "dotenv-expand": "^5.1.0", + "json5": "^2.2.0", + "msgpackr": "^1.5.4", + "nullthrows": "^1.1.1", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/diagnostic": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.8.0.tgz", + "integrity": "sha512-ERnk0zDvm0jQUSj1M+2PLiwVC6nWrtuFEuye6VGuxRDcp9NHbz6gwApeEYxFkPsb3TQPhNjnXXm5nmAw1bpWWw==", + "dev": true, + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/events": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.8.0.tgz", + "integrity": "sha512-xqSZYY3oONM4IZm9+vhyFqX+KFIl145veIczUikwGJlcJZQfAAw736syPx6ecpB+m1EVg3AlvJWy7Lmel4Ak+Q==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/fs": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.8.0.tgz", + "integrity": "sha512-v3DbJlpl8v2/VRlZPw7cy+0myi0YfLblGZcwDvqIsWS35qyxD2rmtYV8u1BusonbgmJeaKiopSECmJkumt0jCw==", + "dev": true, + "dependencies": { + "@parcel/fs-search": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/watcher": "^2.0.7", + "@parcel/workers": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/fs-search": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.8.0.tgz", + "integrity": "sha512-yo7/Y8DCFlhOlIBb5SsRDTkM+7g0DY9sK57iw3hn2z1tGoIiIRptrieImFYSizs7HfDwDY/PMLfORmUdoReDzQ==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/graph": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.8.0.tgz", + "integrity": "sha512-JvAyvBpGmhZ30bi+hStQr52eu+InfJBoiN9Z/32byIWhXEl02EAOwfsPqAe+FGCsdgXnnCGg5F9ZCqwzZ9dwbw==", + "dev": true, + "dependencies": { + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/hash": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.8.0.tgz", + "integrity": "sha512-KV1+96t7Nukth5K7ldUXjVr8ZTH9Dohl49K0Tc+5Qkysif0OxwcDtpVDmcnrUnWmqdBX0AdoLY0Q2Nnly89n/w==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3", + "xxhash-wasm": "^0.4.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/logger": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.8.0.tgz", + "integrity": "sha512-W+7rKsLxLUX6xRmP8PhGWcG48PqrzTPeMWpgSds5nXxAHEFh4cYbkwPKGoTU65a9xUDVyqNreHNIKyizgwAZHQ==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.8.0.tgz", + "integrity": "sha512-xItzXmc3btFhJXsIbE946iaqE6STd2xe5H0zSIaZVXEeucCtMzcd4hxRELquxPstlrAOrrp/lrRpbAlMhso9iA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/namer-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.8.0.tgz", + "integrity": "sha512-cVCx2kJA/Bv7O9pVad1UOibaybR/B+QdWV8Ols8HH4lC2gyjLBXEIR0uuPSEbkGwMEcofG6zA3MwsoPa6r5lBg==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.8.0.tgz", + "integrity": "sha512-cECSh08NSRt1csmmMeKxlnO6ZhXRTuRijkHKFa4iG5hPL+3Cu04YGhuK/QWlP5vNCPVrH3ISlhzlPU5fAi/nEg==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.8.0.tgz", + "integrity": "sha512-T5r3gZVm1xFw6l//iLkzLDUvFzNTUvL5kAtyU5gS5yH/dg7eCS09Km/c2anViQnmXwFUt7zIlBovj1doxAVNSw==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.16.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-htmlnano": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.0.tgz", + "integrity": "sha512-NxEKTRvue/WAU+XbQGfNIU6c7chDekdkwwv9YnCxHEOhnBu4Ok+2tdmCtPuA+4UUNszGxXlaHMnqSrjmqX2S6Q==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-image": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.8.0.tgz", + "integrity": "sha512-66eSoCCGZVRiY6U4OqqYrhQcBcHI9cOkIEbxadZYOF4cJhsskjUDJR0jLb4j2PE6QxUNYlyj5OglQqRLwhz7vA==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.0.tgz", + "integrity": "sha512-qQzM32CzJJuniFaTZDspVn/Vtz/PJ/f89+FckLpWZJVWNihgwTHC1/F0YTDH8g6czNw5ZijwQ3xBVuJQYyIXsQ==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-terser": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.8.0.tgz", + "integrity": "sha512-slS6GWQ3u418WtJmlqlA5Njljcq4OaEdDDR2ifEwltG8POv+hsvD5AAoM2XB0GJwY97TQtdMbBu2DuDF3yM/1Q==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "terser": "^5.2.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/package-manager": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.8.0.tgz", + "integrity": "sha512-n4FgerAX1lTKKTgxmiocnos47Y+b0L60iwU6Q4cC2n4KQNRuNyfhxFXwWcqHstR9wa72JgPaDgo4k0l3Bk8FZw==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/packager-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.8.0.tgz", + "integrity": "sha512-tv/Bto0P6fXjqQ9uCZ8/6b/+38Zr/N2MC7/Nbflzww/lp0k2+kkE9MVJJDr5kST/SzTBRrhbDo+yTbtdZikJYg==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.8.0.tgz", + "integrity": "sha512-4x09v/bt767rxwGTuEw82CjheoOtIKNu4sx1gqwQOz9QowKPniXOIaD+0XmLiARdzRErucf0sL19QHfNcPAhUw==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.8.0.tgz", + "integrity": "sha512-Tn2EtWM1TEdj4t5pt0QjBDzqrXrfRTL3WsdMipZwDSuX04KS0jedJINHjh46HOMwyfJxLbUg3xkGX7F5mYQj5g==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.8.0.tgz", + "integrity": "sha512-s3VniER3X2oNTlfytBGIQF+UZFVNLFWuVu1IkZ8Wg6uYQffrExDlbNDcmFCDcfvcejL3Ch5igP+L6N00f6+wAQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-svg": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.8.0.tgz", + "integrity": "sha512-+BSpdPiNjlAne28nOjG2AyiOejAehe/+X9MxL2FIpPP7UBLNc2ekaM0mDTR5iY45YtZa57oyErBT/U6wZ1TCjw==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "posthtml": "^0.16.4" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/plugin": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.8.0.tgz", + "integrity": "sha512-Tsf+7nDg7KauvTVY6rGc7CmgJruKSwJ54KJ9s5nYFFP9nfwmyqbayCi9xOxicWU9zIHfuF5Etwf17lcA0oAvzw==", + "dev": true, + "dependencies": { + "@parcel/types": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.8.0.tgz", + "integrity": "sha512-ea4/Lp+2jDbzb/tfTgUKzYU51FK8wcewDoYNr06uL+wvx/vzYIDG0jHfzaOTasREnm7ECDr1Zu2Iknrgk1STqQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "chalk": "^4.1.0", + "term-size": "^2.2.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-dev-server": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.0.tgz", + "integrity": "sha512-wg6hUrQ8vUmvlP2fg8YEzYndmq7hWZ21ZgBv4So1Z65I+Qav85Uox7bjGLCSJwEAjdjFKfhV9RGULGzqh8vcAQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/resolver-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.8.0.tgz", + "integrity": "sha512-kO5W+O3Ql6NXNFS6lvfSSt1R+PxO1atNLYxZdVSM6+QQxRMiztfqzZs//RM+oUp+af6muDSUPlNs+RORX0fing==", + "dev": true, + "dependencies": { + "@parcel/node-resolver-core": "2.8.0", + "@parcel/plugin": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-browser-hmr": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.0.tgz", + "integrity": "sha512-zV5wGGvm1cDwWAzkwPUaKh6inWYKxq67YWY4G396PXLMxddM9SQC1c7iFM60OPnD4A+BMOLOy7N6//20h15Dlg==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.8.0.tgz", + "integrity": "sha512-IwT1rX8ZamoYZv0clfswZemfXcIfk+YXwNsqXwzzh6TaMGagj/ZZl1llkn7ERQFq4EoLEoDGGkxqsrJjBp9NDQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.0.tgz", + "integrity": "sha512-a6uuZWkl+mJur2WLZKmpEqq1P06tvRwqGefYbE26DWpwXwU9dLpfnv/nT0hqCmVDHd2TkMyCffolSmq1vY05ew==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "react-error-overlay": "6.0.9", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-service-worker": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.0.tgz", + "integrity": "sha512-Q3Q2O/axQbFi/5Z+BidLB3qhmYdZLTMDagZtsmyH7CktDkZVNV/0UoOGYlqoK06T4cww3XjLSEomXbBu9TlQKQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/source-map": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", + "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": "^12.18.3 || >=14" + } + }, + "node_modules/@parcel/transformer-babel": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.8.0.tgz", + "integrity": "sha512-ie+wFe9pucdnRyX2PTN9amOHrhr/IOwUEAfTz/3dPydOYCuX7ErEngCpI9fBzdYE2AV6/noEwC2Mjeoyz9mT2A==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^5.7.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.8.0.tgz", + "integrity": "sha512-jCMQSfsxCoepblBAHCYMuNWNPQlqasoD6PfNftMdTlv12aUcnjNIYO9600TVLTL799CrEohljbXcfFn6hDGVWw==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.16.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.8.0.tgz", + "integrity": "sha512-KLcZCWSIItZ1s12Sav3uvfTrwhX92craN9u7V3qUs8ld7ompTKsCdnf+gYmeCyISb5yiFDyYBvTGc1bOXvaDRQ==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-image": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.8.0.tgz", + "integrity": "sha512-hJGsZxGlGEkiUvN8kCxA4DhB6/WrHzcIlZZYEgEien9pLctyc6np6idjdcyudPAhH3LwBPkiyeUfCvLAOA1zkA==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/transformer-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.8.0.tgz", + "integrity": "sha512-C5WTkDRiJGBB9tZa1mBsZwsqZjYEKkOa4mdVym3dMokwhFLUga8WtK7kGw4fmXIq41U8ip4orywj+Rd4mvGVWg==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "@swc/helpers": "^0.4.12", + "browserslist": "^4.6.6", + "detect-libc": "^1.0.3", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.13.7", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@parcel/transformer-json": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.8.0.tgz", + "integrity": "sha512-Pp5gROSMpzFDEI6KA2APuSpft6eXZxFgTPV6Xx9pElqseod3iL5+RnpMNV/nv76Ai2bcMEiafus5Pb09vjHgbQ==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "json5": "^2.2.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.8.0.tgz", + "integrity": "sha512-45Ij+cgwXprd1sCLmaMIlCbPz3eEwolGHizgZmXl5l4yjlE2wGyzodhxLpBk1PWu7OxxWRbLnJIlvMYf7Vfw0g==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.0.tgz", + "integrity": "sha512-KrkKBFDW5PNZpr2Ha711eIABQOiJQKvfwfVs3CVpJK5wSADkappDk7CQ0mISPjhamFJ6xx/sNsi7e871I8R9lg==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.8.0.tgz", + "integrity": "sha512-uEbj+kE70vg2Gmdji/AIXPK13s5aQRw7X+xWs3vNpY2oymyMRHbfx1izJFWBh+kxu6Yo6q6qsekkh2rNHEHIUA==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.0.tgz", + "integrity": "sha512-d7G6wBdlwVXLkhC7EO/3UkUOfEOJvsIsQUCEujsrdFF+nfBElXw/TZ+KP8UkmrwMdD0spU/8cKoTyi5k19vt6w==", + "dev": true, + "dependencies": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.8.0.tgz", + "integrity": "sha512-8S6yZoUTCbHOnuWY3M50fscTpI8414945I44fmed+C1e36TnWem8FifuVtGkRZeR8pokF453lmmwWG1eH/4U3w==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/types": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.8.0.tgz", + "integrity": "sha512-DeN3vCnVl9onjtyWxpbP7LwRslVEko4kBaM7yILsuQjEnXmaIOsqIf6FQJOUOPBtQTFFNeQQ2qyf5XoO/rkJ8g==", + "dev": true, + "dependencies": { + "@parcel/cache": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/workers": "2.8.0", + "utility-types": "^3.10.0" + } + }, + "node_modules/@parcel/utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.8.0.tgz", + "integrity": "sha512-r4ACsGtW7zkMUIgwQyOVtPAFiy8L81gbz4tMIRSqyQKnkW7oEHcQ3uN1/LPxj2yfkyQLmhJxmtptLUy9j53rcw==", + "dev": true, + "dependencies": { + "@parcel/codeframe": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/markdown-ansi": "2.8.0", + "@parcel/source-map": "^2.1.1", + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.7.tgz", + "integrity": "sha512-gc3hoS6e+2XdIQ4HHljDB1l0Yx2EWh/sBBtCEFNKGSMlwASWeAQsOY/fPbxOBcZ/pg0jBh4Ga+4xHlZc4faAEQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/workers": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.8.0.tgz", + "integrity": "sha512-vAzoC/wPHLQnyy9P/TrSPftY8F3MhZqPTFi681mxVtLWA3t7wiNlw1zDVKRDP8m5XS1yQOr8Q56CAHyRexhc8g==", + "dev": true, + "dependencies": { + "@parcel/diagnostic": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "chrome-trace-event": "^1.0.2", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.8.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "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.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ffi-napi": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", + "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-uv-event-loop-napi-h": "^1.0.5", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1", + "ref-napi": "^2.0.1 || ^3.0.2", + "ref-struct-di": "^1.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-from-current-process-h": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz", + "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==" + }, + "node_modules/get-uv-event-loop-napi-h": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", + "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", + "dependencies": { + "get-symbol-from-current-process-h": "^1.0.1" + } + }, + "node_modules/globals": { + "version": "13.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", + "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/htmlnano": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.3.tgz", + "integrity": "sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.1", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^5.0.11", + "postcss": "^8.3.11", + "purgecss": "^5.0.0", + "relateurl": "^0.2.7", + "srcset": "4.0.0", + "svgo": "^2.8.0", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.16.1.tgz", + "integrity": "sha512-zU8OTaps3VAodmI2MopfqqOQQ4A9L/2Eo7xoTH/4fNkecy6ftfiGwbbRMTQqtIqJjRg3f927e+lnyBBPhucY1Q==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.16.1", + "lightningcss-darwin-x64": "1.16.1", + "lightningcss-linux-arm-gnueabihf": "1.16.1", + "lightningcss-linux-arm64-gnu": "1.16.1", + "lightningcss-linux-arm64-musl": "1.16.1", + "lightningcss-linux-x64-gnu": "1.16.1", + "lightningcss-linux-x64-musl": "1.16.1", + "lightningcss-win32-x64-msvc": "1.16.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.16.1.tgz", + "integrity": "sha512-/J898YSAiGVqdybHdIF3Ao0Hbh2vyVVj5YNm3NznVzTSvkOi3qQCAtO97sfmNz+bSRHXga7ZPLm+89PpOM5gAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.16.1.tgz", + "integrity": "sha512-vyKCNPRNRqke+5i078V+N0GLfMVLEaNcqIcv28hA/vUNRGk/90EDkDB9EndGay0MoPIrC2y0qE3Y74b/OyedqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.16.1.tgz", + "integrity": "sha512-0AJC52l40VbrzkMJz6qRvlqVVGykkR2MgRS4bLjVC2ab0H0I/n4p6uPZXGvNIt5gw1PedeND/hq+BghNdgfuPQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.16.1.tgz", + "integrity": "sha512-NqxYXsRvI3/Fb9AQLXKrYsU0Q61LqKz5It+Es9gidsfcw1lamny4lmlUgO3quisivkaLCxEkogaizcU6QeZeWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.16.1.tgz", + "integrity": "sha512-VUPQ4dmB9yDQxpJF8/imtwNcbIPzlL6ArLHSUInOGxipDk1lOAklhUjbKUvlL3HVlDwD3WHCxggAY01WpFcjiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.16.1.tgz", + "integrity": "sha512-A40Jjnbellnvh4YF+kt047GLnUU59iLN2LFRCyWQG+QqQZeXOCzXfTQ6EJB4yvHB1mQvWOVdAzVrtEmRw3Vh8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.16.1.tgz", + "integrity": "sha512-VZf76GxW+8mk238tpw0u9R66gBi/m0YB0TvD54oeGiOqvTZ/mabkBkbsuXTSWcKYj8DSrLW+A42qu+6PLRsIgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.16.1.tgz", + "integrity": "sha512-Djy+UzlTtJMayVJU3eFuUW5Gdo+zVTNPJhlYw25tNC9HAoMCkIdSDDrGsWEdEyibEV7xwB8ySTmLuxilfhBtgg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lmdb": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.2.tgz", + "integrity": "sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.5.4", + "node-addon-api": "^4.3.0", + "node-gyp-build-optional-packages": "5.0.3", + "ordered-binary": "^1.2.4", + "weak-lru-cache": "^1.2.2" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "2.5.2", + "@lmdb/lmdb-darwin-x64": "2.5.2", + "@lmdb/lmdb-linux-arm": "2.5.2", + "@lmdb/lmdb-linux-arm64": "2.5.2", + "@lmdb/lmdb-linux-x64": "2.5.2", + "@lmdb/lmdb-win32-x64": "2.5.2" + } + }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/msgpackr": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.0.tgz", + "integrity": "sha512-1Cos3r86XACdjLVY4CN8r72Cgs5lUzxSON6yb81sNZP9vC9nnBrEbu1/ldBhuR9BKejtoYV5C9UhmYUvZFJSNQ==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^2.2.0" + } + }, + "node_modules/msgpackr-extract": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz", + "integrity": "sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.0.3" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-arm": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-x64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-win32-x64": "2.2.0" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz", + "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==", + "dev": true, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "dev": true + }, + "node_modules/ordered-binary": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.4.0.tgz", + "integrity": "sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==", + "dev": true + }, + "node_modules/parcel": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.8.0.tgz", + "integrity": "sha512-p7Fo75CeMw5HC1luovYpBjzPbAJv/Gn7lxcs4f0LxcwBCWbkQ73zHgJXJQqnM38qQABEYEiQq6000+j+k5U/Mw==", + "dev": true, + "dependencies": { + "@parcel/config-default": "2.8.0", + "@parcel/core": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/reporter-cli": "2.8.0", + "@parcel/reporter-dev-server": "2.8.0", + "@parcel/utils": "2.8.0", + "chalk": "^4.1.0", + "commander": "^7.0.0", + "get-port": "^4.2.0", + "v8-compile-cache": "^2.0.0" + }, + "bin": { + "parcel": "lib/bin.js" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/parcel/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "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/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dev": true, + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dev": true, + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dev": true, + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml/node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prettier": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", + "dev": true + }, + "node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ref-napi": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", + "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-symbol-from-current-process-h": "^1.0.2", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/ref-struct-di": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", + "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", + "dependencies": { + "debug": "^3.1.0" + } + }, + "node_modules/ref-struct-di/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", + "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "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.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, + "node_modules/xxhash-wasm": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", + "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@lezer/common": { + "version": "0.15.12", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", + "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==", + "dev": true + }, + "@lezer/lr": { + "version": "0.15.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz", + "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==", + "dev": true, + "requires": { + "@lezer/common": "^0.15.0" + } + }, + "@lmdb/lmdb-darwin-arm64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz", + "integrity": "sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==", + "dev": true, + "optional": true + }, + "@lmdb/lmdb-darwin-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz", + "integrity": "sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==", + "dev": true, + "optional": true + }, + "@lmdb/lmdb-linux-arm": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz", + "integrity": "sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==", + "dev": true, + "optional": true + }, + "@lmdb/lmdb-linux-arm64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz", + "integrity": "sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==", + "dev": true, + "optional": true + }, + "@lmdb/lmdb-linux-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz", + "integrity": "sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==", + "dev": true, + "optional": true + }, + "@lmdb/lmdb-win32-x64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz", + "integrity": "sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==", + "dev": true, + "optional": true + }, + "@mischnic/json-sourcemap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz", + "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==", + "dev": true, + "requires": { + "@lezer/common": "^0.15.7", + "@lezer/lr": "^0.15.4", + "json5": "^2.2.1" + } + }, + "@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==", + "dev": true, + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==", + "dev": true, + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==", + "dev": true, + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==", + "dev": true, + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==", + "dev": true, + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==", + "dev": true, + "optional": true + }, + "@parcel/bundler-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.8.0.tgz", + "integrity": "sha512-OvDDhxX4LwfGe7lYVMbJMzqNcDk8ydOqNw0Hra9WPgl0m5gju/eVIbDvot3JXp5F96FmV36uCxdODJhKTNoAzQ==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/graph": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/cache": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.8.0.tgz", + "integrity": "sha512-k945hrafMDR2wyCKyZYgwypeLLuZWce6FzhgunI4taBUeVnNCcpFAWzbfOVQ39SqZTGDqG3MNT+VuehssHXxyg==", + "dev": true, + "requires": { + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/utils": "2.8.0", + "lmdb": "2.5.2" + } + }, + "@parcel/codeframe": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.8.0.tgz", + "integrity": "sha512-821d+KVcpEvJNMj9WMC39xXZK6zvRS/HUjQag2f3DkcRcZwk1uXJZdW6p1EB7C3e4e/0KSK3NTSVGEvbOSR+9w==", + "dev": true, + "requires": { + "chalk": "^4.1.0" + } + }, + "@parcel/compressor-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.8.0.tgz", + "integrity": "sha512-tM49t0gDQnwJbrDCeoCn9LRc8inZ/TSPQTttJTfcmFHHFqEllI0ZDVG0AiQw5NOMQbBLYiKun1adXn8pkcPLEA==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0" + } + }, + "@parcel/config-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.8.0.tgz", + "integrity": "sha512-j9g50QNSLjuNpY0TP01EgGJPxWNes9d+e8+N07Z5Wv0u+UUnJ2uIOpo7PVn7ullOGhm1f9lP4KsJenu5gWb+cg==", + "dev": true, + "requires": { + "@parcel/bundler-default": "2.8.0", + "@parcel/compressor-raw": "2.8.0", + "@parcel/namer-default": "2.8.0", + "@parcel/optimizer-css": "2.8.0", + "@parcel/optimizer-htmlnano": "2.8.0", + "@parcel/optimizer-image": "2.8.0", + "@parcel/optimizer-svgo": "2.8.0", + "@parcel/optimizer-terser": "2.8.0", + "@parcel/packager-css": "2.8.0", + "@parcel/packager-html": "2.8.0", + "@parcel/packager-js": "2.8.0", + "@parcel/packager-raw": "2.8.0", + "@parcel/packager-svg": "2.8.0", + "@parcel/reporter-dev-server": "2.8.0", + "@parcel/resolver-default": "2.8.0", + "@parcel/runtime-browser-hmr": "2.8.0", + "@parcel/runtime-js": "2.8.0", + "@parcel/runtime-react-refresh": "2.8.0", + "@parcel/runtime-service-worker": "2.8.0", + "@parcel/transformer-babel": "2.8.0", + "@parcel/transformer-css": "2.8.0", + "@parcel/transformer-html": "2.8.0", + "@parcel/transformer-image": "2.8.0", + "@parcel/transformer-js": "2.8.0", + "@parcel/transformer-json": "2.8.0", + "@parcel/transformer-postcss": "2.8.0", + "@parcel/transformer-posthtml": "2.8.0", + "@parcel/transformer-raw": "2.8.0", + "@parcel/transformer-react-refresh-wrap": "2.8.0", + "@parcel/transformer-svg": "2.8.0" + } + }, + "@parcel/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.8.0.tgz", + "integrity": "sha512-udzbe3jjbpfKlRE9pdlROAa+lvAjS1L/AzN6r2j1y/Fsn7ze/NfvnCFw6o2YNIrXg002aQ7M1St/x1fdGfmVKA==", + "dev": true, + "requires": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/cache": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/graph": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "abortcontroller-polyfill": "^1.1.9", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^7.0.0", + "dotenv-expand": "^5.1.0", + "json5": "^2.2.0", + "msgpackr": "^1.5.4", + "nullthrows": "^1.1.1", + "semver": "^5.7.1" + } + }, + "@parcel/diagnostic": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.8.0.tgz", + "integrity": "sha512-ERnk0zDvm0jQUSj1M+2PLiwVC6nWrtuFEuye6VGuxRDcp9NHbz6gwApeEYxFkPsb3TQPhNjnXXm5nmAw1bpWWw==", + "dev": true, + "requires": { + "@mischnic/json-sourcemap": "^0.1.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/events": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.8.0.tgz", + "integrity": "sha512-xqSZYY3oONM4IZm9+vhyFqX+KFIl145veIczUikwGJlcJZQfAAw736syPx6ecpB+m1EVg3AlvJWy7Lmel4Ak+Q==", + "dev": true + }, + "@parcel/fs": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.8.0.tgz", + "integrity": "sha512-v3DbJlpl8v2/VRlZPw7cy+0myi0YfLblGZcwDvqIsWS35qyxD2rmtYV8u1BusonbgmJeaKiopSECmJkumt0jCw==", + "dev": true, + "requires": { + "@parcel/fs-search": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/watcher": "^2.0.7", + "@parcel/workers": "2.8.0" + } + }, + "@parcel/fs-search": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.8.0.tgz", + "integrity": "sha512-yo7/Y8DCFlhOlIBb5SsRDTkM+7g0DY9sK57iw3hn2z1tGoIiIRptrieImFYSizs7HfDwDY/PMLfORmUdoReDzQ==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } + }, + "@parcel/graph": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.8.0.tgz", + "integrity": "sha512-JvAyvBpGmhZ30bi+hStQr52eu+InfJBoiN9Z/32byIWhXEl02EAOwfsPqAe+FGCsdgXnnCGg5F9ZCqwzZ9dwbw==", + "dev": true, + "requires": { + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/hash": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.8.0.tgz", + "integrity": "sha512-KV1+96t7Nukth5K7ldUXjVr8ZTH9Dohl49K0Tc+5Qkysif0OxwcDtpVDmcnrUnWmqdBX0AdoLY0Q2Nnly89n/w==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "xxhash-wasm": "^0.4.2" + } + }, + "@parcel/logger": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.8.0.tgz", + "integrity": "sha512-W+7rKsLxLUX6xRmP8PhGWcG48PqrzTPeMWpgSds5nXxAHEFh4cYbkwPKGoTU65a9xUDVyqNreHNIKyizgwAZHQ==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0" + } + }, + "@parcel/markdown-ansi": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.8.0.tgz", + "integrity": "sha512-xItzXmc3btFhJXsIbE946iaqE6STd2xe5H0zSIaZVXEeucCtMzcd4hxRELquxPstlrAOrrp/lrRpbAlMhso9iA==", + "dev": true, + "requires": { + "chalk": "^4.1.0" + } + }, + "@parcel/namer-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.8.0.tgz", + "integrity": "sha512-cVCx2kJA/Bv7O9pVad1UOibaybR/B+QdWV8Ols8HH4lC2gyjLBXEIR0uuPSEbkGwMEcofG6zA3MwsoPa6r5lBg==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/node-resolver-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.8.0.tgz", + "integrity": "sha512-cECSh08NSRt1csmmMeKxlnO6ZhXRTuRijkHKFa4iG5hPL+3Cu04YGhuK/QWlP5vNCPVrH3ISlhzlPU5fAi/nEg==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "semver": "^5.7.1" + } + }, + "@parcel/optimizer-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.8.0.tgz", + "integrity": "sha512-T5r3gZVm1xFw6l//iLkzLDUvFzNTUvL5kAtyU5gS5yH/dg7eCS09Km/c2anViQnmXwFUt7zIlBovj1doxAVNSw==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.16.1", + "nullthrows": "^1.1.1" + } + }, + "@parcel/optimizer-htmlnano": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.0.tgz", + "integrity": "sha512-NxEKTRvue/WAU+XbQGfNIU6c7chDekdkwwv9YnCxHEOhnBu4Ok+2tdmCtPuA+4UUNszGxXlaHMnqSrjmqX2S6Q==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "svgo": "^2.4.0" + } + }, + "@parcel/optimizer-image": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.8.0.tgz", + "integrity": "sha512-66eSoCCGZVRiY6U4OqqYrhQcBcHI9cOkIEbxadZYOF4cJhsskjUDJR0jLb4j2PE6QxUNYlyj5OglQqRLwhz7vA==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "detect-libc": "^1.0.3" + } + }, + "@parcel/optimizer-svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.0.tgz", + "integrity": "sha512-qQzM32CzJJuniFaTZDspVn/Vtz/PJ/f89+FckLpWZJVWNihgwTHC1/F0YTDH8g6czNw5ZijwQ3xBVuJQYyIXsQ==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "svgo": "^2.4.0" + } + }, + "@parcel/optimizer-terser": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.8.0.tgz", + "integrity": "sha512-slS6GWQ3u418WtJmlqlA5Njljcq4OaEdDDR2ifEwltG8POv+hsvD5AAoM2XB0GJwY97TQtdMbBu2DuDF3yM/1Q==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "terser": "^5.2.0" + } + }, + "@parcel/package-manager": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.8.0.tgz", + "integrity": "sha512-n4FgerAX1lTKKTgxmiocnos47Y+b0L60iwU6Q4cC2n4KQNRuNyfhxFXwWcqHstR9wa72JgPaDgo4k0l3Bk8FZw==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "semver": "^5.7.1" + } + }, + "@parcel/packager-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.8.0.tgz", + "integrity": "sha512-tv/Bto0P6fXjqQ9uCZ8/6b/+38Zr/N2MC7/Nbflzww/lp0k2+kkE9MVJJDr5kST/SzTBRrhbDo+yTbtdZikJYg==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/packager-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.8.0.tgz", + "integrity": "sha512-4x09v/bt767rxwGTuEw82CjheoOtIKNu4sx1gqwQOz9QowKPniXOIaD+0XmLiARdzRErucf0sL19QHfNcPAhUw==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + } + }, + "@parcel/packager-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.8.0.tgz", + "integrity": "sha512-Tn2EtWM1TEdj4t5pt0QjBDzqrXrfRTL3WsdMipZwDSuX04KS0jedJINHjh46HOMwyfJxLbUg3xkGX7F5mYQj5g==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/packager-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.8.0.tgz", + "integrity": "sha512-s3VniER3X2oNTlfytBGIQF+UZFVNLFWuVu1IkZ8Wg6uYQffrExDlbNDcmFCDcfvcejL3Ch5igP+L6N00f6+wAQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0" + } + }, + "@parcel/packager-svg": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.8.0.tgz", + "integrity": "sha512-+BSpdPiNjlAne28nOjG2AyiOejAehe/+X9MxL2FIpPP7UBLNc2ekaM0mDTR5iY45YtZa57oyErBT/U6wZ1TCjw==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "posthtml": "^0.16.4" + } + }, + "@parcel/plugin": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.8.0.tgz", + "integrity": "sha512-Tsf+7nDg7KauvTVY6rGc7CmgJruKSwJ54KJ9s5nYFFP9nfwmyqbayCi9xOxicWU9zIHfuF5Etwf17lcA0oAvzw==", + "dev": true, + "requires": { + "@parcel/types": "2.8.0" + } + }, + "@parcel/reporter-cli": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.8.0.tgz", + "integrity": "sha512-ea4/Lp+2jDbzb/tfTgUKzYU51FK8wcewDoYNr06uL+wvx/vzYIDG0jHfzaOTasREnm7ECDr1Zu2Iknrgk1STqQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "chalk": "^4.1.0", + "term-size": "^2.2.1" + } + }, + "@parcel/reporter-dev-server": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.0.tgz", + "integrity": "sha512-wg6hUrQ8vUmvlP2fg8YEzYndmq7hWZ21ZgBv4So1Z65I+Qav85Uox7bjGLCSJwEAjdjFKfhV9RGULGzqh8vcAQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0" + } + }, + "@parcel/resolver-default": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.8.0.tgz", + "integrity": "sha512-kO5W+O3Ql6NXNFS6lvfSSt1R+PxO1atNLYxZdVSM6+QQxRMiztfqzZs//RM+oUp+af6muDSUPlNs+RORX0fing==", + "dev": true, + "requires": { + "@parcel/node-resolver-core": "2.8.0", + "@parcel/plugin": "2.8.0" + } + }, + "@parcel/runtime-browser-hmr": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.0.tgz", + "integrity": "sha512-zV5wGGvm1cDwWAzkwPUaKh6inWYKxq67YWY4G396PXLMxddM9SQC1c7iFM60OPnD4A+BMOLOy7N6//20h15Dlg==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0" + } + }, + "@parcel/runtime-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.8.0.tgz", + "integrity": "sha512-IwT1rX8ZamoYZv0clfswZemfXcIfk+YXwNsqXwzzh6TaMGagj/ZZl1llkn7ERQFq4EoLEoDGGkxqsrJjBp9NDQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/runtime-react-refresh": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.0.tgz", + "integrity": "sha512-a6uuZWkl+mJur2WLZKmpEqq1P06tvRwqGefYbE26DWpwXwU9dLpfnv/nT0hqCmVDHd2TkMyCffolSmq1vY05ew==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "react-error-overlay": "6.0.9", + "react-refresh": "^0.9.0" + } + }, + "@parcel/runtime-service-worker": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.0.tgz", + "integrity": "sha512-Q3Q2O/axQbFi/5Z+BidLB3qhmYdZLTMDagZtsmyH7CktDkZVNV/0UoOGYlqoK06T4cww3XjLSEomXbBu9TlQKQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/source-map": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", + "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } + }, + "@parcel/transformer-babel": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.8.0.tgz", + "integrity": "sha512-ie+wFe9pucdnRyX2PTN9amOHrhr/IOwUEAfTz/3dPydOYCuX7ErEngCpI9fBzdYE2AV6/noEwC2Mjeoyz9mT2A==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^5.7.0" + } + }, + "@parcel/transformer-css": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.8.0.tgz", + "integrity": "sha512-jCMQSfsxCoepblBAHCYMuNWNPQlqasoD6PfNftMdTlv12aUcnjNIYO9600TVLTL799CrEohljbXcfFn6hDGVWw==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.16.1", + "nullthrows": "^1.1.1" + } + }, + "@parcel/transformer-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.8.0.tgz", + "integrity": "sha512-KLcZCWSIItZ1s12Sav3uvfTrwhX92craN9u7V3qUs8ld7ompTKsCdnf+gYmeCyISb5yiFDyYBvTGc1bOXvaDRQ==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + } + }, + "@parcel/transformer-image": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.8.0.tgz", + "integrity": "sha512-hJGsZxGlGEkiUvN8kCxA4DhB6/WrHzcIlZZYEgEien9pLctyc6np6idjdcyudPAhH3LwBPkiyeUfCvLAOA1zkA==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "nullthrows": "^1.1.1" + } + }, + "@parcel/transformer-js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.8.0.tgz", + "integrity": "sha512-C5WTkDRiJGBB9tZa1mBsZwsqZjYEKkOa4mdVym3dMokwhFLUga8WtK7kGw4fmXIq41U8ip4orywj+Rd4mvGVWg==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.8.0", + "@parcel/workers": "2.8.0", + "@swc/helpers": "^0.4.12", + "browserslist": "^4.6.6", + "detect-libc": "^1.0.3", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.13.7", + "semver": "^5.7.1" + } + }, + "@parcel/transformer-json": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.8.0.tgz", + "integrity": "sha512-Pp5gROSMpzFDEI6KA2APuSpft6eXZxFgTPV6Xx9pElqseod3iL5+RnpMNV/nv76Ai2bcMEiafus5Pb09vjHgbQ==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "json5": "^2.2.0" + } + }, + "@parcel/transformer-postcss": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.8.0.tgz", + "integrity": "sha512-45Ij+cgwXprd1sCLmaMIlCbPz3eEwolGHizgZmXl5l4yjlE2wGyzodhxLpBk1PWu7OxxWRbLnJIlvMYf7Vfw0g==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^5.7.1" + } + }, + "@parcel/transformer-posthtml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.0.tgz", + "integrity": "sha512-KrkKBFDW5PNZpr2Ha711eIABQOiJQKvfwfVs3CVpJK5wSADkappDk7CQ0mISPjhamFJ6xx/sNsi7e871I8R9lg==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + } + }, + "@parcel/transformer-raw": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.8.0.tgz", + "integrity": "sha512-uEbj+kE70vg2Gmdji/AIXPK13s5aQRw7X+xWs3vNpY2oymyMRHbfx1izJFWBh+kxu6Yo6q6qsekkh2rNHEHIUA==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0" + } + }, + "@parcel/transformer-react-refresh-wrap": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.0.tgz", + "integrity": "sha512-d7G6wBdlwVXLkhC7EO/3UkUOfEOJvsIsQUCEujsrdFF+nfBElXw/TZ+KP8UkmrwMdD0spU/8cKoTyi5k19vt6w==", + "dev": true, + "requires": { + "@parcel/plugin": "2.8.0", + "@parcel/utils": "2.8.0", + "react-refresh": "^0.9.0" + } + }, + "@parcel/transformer-svg": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.8.0.tgz", + "integrity": "sha512-8S6yZoUTCbHOnuWY3M50fscTpI8414945I44fmed+C1e36TnWem8FifuVtGkRZeR8pokF453lmmwWG1eH/4U3w==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/plugin": "2.8.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + } + }, + "@parcel/types": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.8.0.tgz", + "integrity": "sha512-DeN3vCnVl9onjtyWxpbP7LwRslVEko4kBaM7yILsuQjEnXmaIOsqIf6FQJOUOPBtQTFFNeQQ2qyf5XoO/rkJ8g==", + "dev": true, + "requires": { + "@parcel/cache": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/source-map": "^2.1.1", + "@parcel/workers": "2.8.0", + "utility-types": "^3.10.0" + } + }, + "@parcel/utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.8.0.tgz", + "integrity": "sha512-r4ACsGtW7zkMUIgwQyOVtPAFiy8L81gbz4tMIRSqyQKnkW7oEHcQ3uN1/LPxj2yfkyQLmhJxmtptLUy9j53rcw==", + "dev": true, + "requires": { + "@parcel/codeframe": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/hash": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/markdown-ansi": "2.8.0", + "@parcel/source-map": "^2.1.1", + "chalk": "^4.1.0" + } + }, + "@parcel/watcher": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.7.tgz", + "integrity": "sha512-gc3hoS6e+2XdIQ4HHljDB1l0Yx2EWh/sBBtCEFNKGSMlwASWeAQsOY/fPbxOBcZ/pg0jBh4Ga+4xHlZc4faAEQ==", + "dev": true, + "requires": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + } + }, + "@parcel/workers": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.8.0.tgz", + "integrity": "sha512-vAzoC/wPHLQnyy9P/TrSPftY8F3MhZqPTFi681mxVtLWA3t7wiNlw1zDVKRDP8m5XS1yQOr8Q56CAHyRexhc8g==", + "dev": true, + "requires": { + "@parcel/diagnostic": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/types": "2.8.0", + "@parcel/utils": "2.8.0", + "chrome-trace-event": "^1.0.2", + "nullthrows": "^1.1.1" + } + }, + "@swc/helpers": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "dev": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==", + "dev": true + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==" + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "ffi-napi": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", + "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", + "requires": { + "debug": "^4.1.1", + "get-uv-event-loop-napi-h": "^1.0.5", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1", + "ref-napi": "^2.0.1 || ^3.0.2", + "ref-struct-di": "^1.1.0" + } + }, + "get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "dev": true + }, + "get-symbol-from-current-process-h": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz", + "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==" + }, + "get-uv-event-loop-napi-h": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", + "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", + "requires": { + "get-symbol-from-current-process-h": "^1.0.1" + } + }, + "globals": { + "version": "13.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", + "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "htmlnano": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.3.tgz", + "integrity": "sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.1", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + } + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "lightningcss": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.16.1.tgz", + "integrity": "sha512-zU8OTaps3VAodmI2MopfqqOQQ4A9L/2Eo7xoTH/4fNkecy6ftfiGwbbRMTQqtIqJjRg3f927e+lnyBBPhucY1Q==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "lightningcss-darwin-arm64": "1.16.1", + "lightningcss-darwin-x64": "1.16.1", + "lightningcss-linux-arm-gnueabihf": "1.16.1", + "lightningcss-linux-arm64-gnu": "1.16.1", + "lightningcss-linux-arm64-musl": "1.16.1", + "lightningcss-linux-x64-gnu": "1.16.1", + "lightningcss-linux-x64-musl": "1.16.1", + "lightningcss-win32-x64-msvc": "1.16.1" + } + }, + "lightningcss-darwin-arm64": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.16.1.tgz", + "integrity": "sha512-/J898YSAiGVqdybHdIF3Ao0Hbh2vyVVj5YNm3NznVzTSvkOi3qQCAtO97sfmNz+bSRHXga7ZPLm+89PpOM5gAg==", + "dev": true, + "optional": true + }, + "lightningcss-darwin-x64": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.16.1.tgz", + "integrity": "sha512-vyKCNPRNRqke+5i078V+N0GLfMVLEaNcqIcv28hA/vUNRGk/90EDkDB9EndGay0MoPIrC2y0qE3Y74b/OyedqQ==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm-gnueabihf": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.16.1.tgz", + "integrity": "sha512-0AJC52l40VbrzkMJz6qRvlqVVGykkR2MgRS4bLjVC2ab0H0I/n4p6uPZXGvNIt5gw1PedeND/hq+BghNdgfuPQ==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-gnu": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.16.1.tgz", + "integrity": "sha512-NqxYXsRvI3/Fb9AQLXKrYsU0Q61LqKz5It+Es9gidsfcw1lamny4lmlUgO3quisivkaLCxEkogaizcU6QeZeWQ==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-musl": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.16.1.tgz", + "integrity": "sha512-VUPQ4dmB9yDQxpJF8/imtwNcbIPzlL6ArLHSUInOGxipDk1lOAklhUjbKUvlL3HVlDwD3WHCxggAY01WpFcjiA==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-gnu": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.16.1.tgz", + "integrity": "sha512-A40Jjnbellnvh4YF+kt047GLnUU59iLN2LFRCyWQG+QqQZeXOCzXfTQ6EJB4yvHB1mQvWOVdAzVrtEmRw3Vh8g==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-musl": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.16.1.tgz", + "integrity": "sha512-VZf76GxW+8mk238tpw0u9R66gBi/m0YB0TvD54oeGiOqvTZ/mabkBkbsuXTSWcKYj8DSrLW+A42qu+6PLRsIgA==", + "dev": true, + "optional": true + }, + "lightningcss-win32-x64-msvc": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.16.1.tgz", + "integrity": "sha512-Djy+UzlTtJMayVJU3eFuUW5Gdo+zVTNPJhlYw25tNC9HAoMCkIdSDDrGsWEdEyibEV7xwB8ySTmLuxilfhBtgg==", + "dev": true, + "optional": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lmdb": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.2.tgz", + "integrity": "sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==", + "dev": true, + "requires": { + "@lmdb/lmdb-darwin-arm64": "2.5.2", + "@lmdb/lmdb-darwin-x64": "2.5.2", + "@lmdb/lmdb-linux-arm": "2.5.2", + "@lmdb/lmdb-linux-arm64": "2.5.2", + "@lmdb/lmdb-linux-x64": "2.5.2", + "@lmdb/lmdb-win32-x64": "2.5.2", + "msgpackr": "^1.5.4", + "node-addon-api": "^4.3.0", + "node-gyp-build-optional-packages": "5.0.3", + "ordered-binary": "^1.2.4", + "weak-lru-cache": "^1.2.2" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + } + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "msgpackr": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.0.tgz", + "integrity": "sha512-1Cos3r86XACdjLVY4CN8r72Cgs5lUzxSON6yb81sNZP9vC9nnBrEbu1/ldBhuR9BKejtoYV5C9UhmYUvZFJSNQ==", + "dev": true, + "requires": { + "msgpackr-extract": "^2.2.0" + } + }, + "msgpackr-extract": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz", + "integrity": "sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==", + "dev": true, + "optional": true, + "requires": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-arm": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-linux-x64": "2.2.0", + "@msgpackr-extract/msgpackr-extract-win32-x64": "2.2.0", + "node-gyp-build-optional-packages": "5.0.3" + } + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" + }, + "node-gyp-build-optional-packages": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz", + "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "dev": true + }, + "ordered-binary": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.4.0.tgz", + "integrity": "sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==", + "dev": true + }, + "parcel": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.8.0.tgz", + "integrity": "sha512-p7Fo75CeMw5HC1luovYpBjzPbAJv/Gn7lxcs4f0LxcwBCWbkQ73zHgJXJQqnM38qQABEYEiQq6000+j+k5U/Mw==", + "dev": true, + "requires": { + "@parcel/config-default": "2.8.0", + "@parcel/core": "2.8.0", + "@parcel/diagnostic": "2.8.0", + "@parcel/events": "2.8.0", + "@parcel/fs": "2.8.0", + "@parcel/logger": "2.8.0", + "@parcel/package-manager": "2.8.0", + "@parcel/reporter-cli": "2.8.0", + "@parcel/reporter-dev-server": "2.8.0", + "@parcel/utils": "2.8.0", + "chalk": "^4.1.0", + "commander": "^7.0.0", + "get-port": "^4.2.0", + "v8-compile-cache": "^2.0.0" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@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" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dev": true, + "requires": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "dependencies": { + "posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, + "requires": { + "htmlparser2": "^7.1.1" + } + } + } + }, + "posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dev": true, + "requires": { + "htmlparser2": "^7.1.1" + } + }, + "posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dev": true, + "requires": { + "is-json": "^2.0.1" + } + }, + "prettier": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", + "dev": true + }, + "react-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", + "dev": true + }, + "react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "dev": true + }, + "ref-napi": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", + "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", + "requires": { + "debug": "^4.1.1", + "get-symbol-from-current-process-h": "^1.0.2", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1" + } + }, + "ref-struct-di": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", + "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true + }, + "terser": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", + "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, + "xxhash-wasm": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", + "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } + } +} diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..887e789 --- /dev/null +++ b/js/package.json @@ -0,0 +1,27 @@ +{ + "name": "rosettaboy", + "version": "0.0.0", + "description": "A gameboy emulator in lots of languages, JS edition", + "main": "./dist/index.js", + "type": "module", + "bin": { + "rosettaboy": "./dist/index.js" + }, + "scripts": { + "build": "parcel build --public-url . src/index.ts", + "watch": "parcel watch --public-url . src/index.ts", + "format": "prettier --write 'src/*.ts' --tab-width 4 --trailing-comma all" + }, + "author": "Shish ", + "license": "MIT", + "devDependencies": { + "@types/node": "^18.11.9", + "parcel": "^2.8.0", + "prettier": "^2.8.0" + }, + "dependencies": { + "commander": "^9.4.1", + "ffi-napi": "^4.0.3", + "ref-napi": "^3.0.3" + } +} diff --git a/js/shell.nix b/js/shell.nix new file mode 100644 index 0000000..910f043 --- /dev/null +++ b/js/shell.nix @@ -0,0 +1,6 @@ +{ pkgs ? import {} } : pkgs.mkShell { + buildInputs = with pkgs; [ + nodejs + libffi + ]; +} diff --git a/js/src/_sdl.ts b/js/src/_sdl.ts new file mode 100644 index 0000000..c17384d --- /dev/null +++ b/js/src/_sdl.ts @@ -0,0 +1,242 @@ +var ref = require("ref-napi"); +var ffi = require("ffi-napi"); + +export type SDL_Window = { on: CallableFunction }; +export type SDL_Renderer = { }; +export type SDL_Texture = { }; +export type SDL_Surface = { }; +export type SDL_Point = { x: number; y: number }; +export type SDL_Rect = { x: number; y: number; w: number; h: number }; +export type SDL_Color = { r: u8; g: u8; b: u8; a: u8 }; + +export type KeyUp = { key: string }; +export type KeyDown = { key: string }; +export type Window = {}; + +// c-types / c-pointers +export const ctSDL_Window = ref.types.void; +export const cpSDL_Window = ref.refType(ctSDL_Window); +export const ctSDL_Renderer = ref.types.void; +export const cpSDL_Renderer = ref.refType(ctSDL_Renderer); +export const ctSDL_Surface = ref.types.void; +export const cpSDL_Surface = ref.refType(ctSDL_Surface); +export const ctSDL_Texture = ref.types.void; +export const cpSDL_Texture = ref.refType(ctSDL_Texture); + +const int = "int"; +export const f = ffi.Library("libSDL2", { + // SDL.h + SDL_Init: [int, [int]], + // SDL_video.h + SDL_CreateWindow: [cpSDL_Window, ["string", int, int, int, int, int]], + // SDL_hints.h + SDL_SetHint: ["bool", ["string", "string"]], + // SDL_render.h + SDL_CreateRenderer: [cpSDL_Renderer, [cpSDL_Window, int, int]], + SDL_RenderSetLogicalSize: [int, [cpSDL_Renderer, int, int]], + SDL_CreateTexture: [cpSDL_Texture, [cpSDL_Renderer, int, int, int, int]], + // SDL_surface.h + SDL_CreateRGBSurface: [cpSDL_Surface, [int, int, int, int, int, int, int, int]], + + // SDL_error.h + SDL_GetError: ["string", []], +}); +f.SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000; + +// SDL.h +f.SDL_INIT_TIMER = 0x00000001; +f.SDL_INIT_AUDIO = 0x00000010; +f.SDL_INIT_VIDEO = 0x00000020; /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ +f.SDL_INIT_JOYSTICK = 0x00000200; /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */ +f.SDL_INIT_HAPTIC = 0x00001000; +f.SDL_INIT_GAMECONTROLLER = 0x00002000; /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */ +f.SDL_INIT_EVENTS = 0x00004000; +f.SDL_INIT_SENSOR = 0x00008000; +f.SDL_INIT_NOPARACHUTE = 0x00100000; /**< compatibility; this flag is ignored. */ +f.SDL_INIT_EVERYTHING = + f.SDL_INIT_TIMER | + f.SDL_INIT_AUDIO | + f.SDL_INIT_VIDEO | + f.SDL_INIT_EVENTS | + f.SDL_INIT_JOYSTICK | + f.SDL_INIT_HAPTIC | + f.SDL_INIT_GAMECONTROLLER | + f.SDL_INIT_SENSOR; + +// SDL_video.h +export enum SDL_WindowFlags { + SDL_WINDOW_FULLSCREEN = 0x00000001, /**< fullscreen window */ + SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ + SDL_WINDOW_SHOWN = 0x00000004, /**< window is visible */ + SDL_WINDOW_HIDDEN = 0x00000008, /**< window is not visible */ + SDL_WINDOW_BORDERLESS = 0x00000010, /**< no window decoration */ + SDL_WINDOW_RESIZABLE = 0x00000020, /**< window can be resized */ + SDL_WINDOW_MINIMIZED = 0x00000040, /**< window is minimized */ + SDL_WINDOW_MAXIMIZED = 0x00000080, /**< window is maximized */ + SDL_WINDOW_MOUSE_GRABBED = 0x00000100, /**< window has grabbed mouse input */ + SDL_WINDOW_INPUT_FOCUS = 0x00000200, /**< window has input focus */ + SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ + SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), + SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ + SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported. + On macOS NSHighResolutionCapable must be set true in the + application's Info.plist for this to have any effect. */ + SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /**< window has mouse captured (unrelated to MOUSE_GRABBED) */ + SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /**< window should always be above others */ + SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /**< window should not be added to the taskbar */ + SDL_WINDOW_UTILITY = 0x00020000, /**< window should be treated as a utility window */ + SDL_WINDOW_TOOLTIP = 0x00040000, /**< window should be treated as a tooltip */ + SDL_WINDOW_POPUP_MENU = 0x00080000, /**< window should be treated as a popup menu */ + SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000, /**< window has grabbed keyboard input */ + SDL_WINDOW_VULKAN = 0x10000000, /**< window usable for Vulkan surface */ + SDL_WINDOW_METAL = 0x20000000, /**< window usable for Metal view */ + + SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED /**< equivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility */ +}; + +// SDL_hints.h +f.SDL_HINT_RENDER_SCALE_QUALITY = "SDL_RENDER_SCALE_QUALITY"; + + +/* +) { + const program = new Command(); + program + .version("0.0.0") + .description("A gameboy emulator in many languages, javascript edition") + .option("-c, --debug-cpu", "Debug CPU") + .option("-g, --debug-gpu", "Debug GPU") + .option("-r, --debug-ram", "Debug RAM") + .option("-H, --headless", "Disable GUI") + .option("-S, --silent", "Disable Sound") + .option("-t, --turbo", "No sleep()") + .option("-f, --frames ", "Exit after this many frames", myParseInt) + .option("-p, --profile ", "Exit after this many seconds", myParseInt) + .argument("rom", "path to .gb file"); + + let parsed = program.parse(argv); + let args = parsed.opts(); + args.rom = parsed.args[0]; + return args; +} diff --git a/js/src/buttons.ts b/js/src/buttons.ts new file mode 100644 index 0000000..8ef2c4b --- /dev/null +++ b/js/src/buttons.ts @@ -0,0 +1,156 @@ +import { CPU } from "./cpu"; +import { Quit } from "./errors"; +import { Interrupt, Mem } from "./consts"; +import * as sdl from "./_sdl"; + +enum Joypad { + MODE_BUTTONS = 1 << 5, + MODE_DPAD = 1 << 4, + DOWN = 1 << 3, + START = 1 << 3, + UP = 1 << 2, + SELECT = 1 << 2, + LEFT = 1 << 1, + B = 1 << 1, + RIGHT = 1 << 0, + A = 1 << 0, +} + +export class Buttons { + cpu: CPU; + turbo: boolean; + cycle: number; + up: boolean; + down: boolean; + left: boolean; + right: boolean; + a: boolean; + b: boolean; + start: boolean; + select: boolean; + + constructor(cpu: CPU, window: sdl.SDL_Window | null) { + this.cpu = cpu; + this.turbo = false; + this.cycle = 0; + this.up = false; + this.down = false; + this.left = false; + this.right = false; + this.a = false; + this.b = false; + this.start = false; + this.select = false; + + /* + if (window) { + window.on("keyDown", this.onKeyDown); + window.on("keyUp", this.onKeyUp); + window.on("close", this.onClose); + } + */ + } + + tick() { + // FIXME: handle inputs + this.cycle += 1; + this.update_buttons(); + } + + update_buttons() { + let JOYP = ~this.cpu.ram.get(Mem.JOYP); + JOYP &= 0x30; + if (JOYP & Joypad.MODE_DPAD) { + if (this.up) JOYP |= Joypad.UP; + if (this.down) JOYP |= Joypad.DOWN; + if (this.left) JOYP |= Joypad.LEFT; + if (this.right) JOYP |= Joypad.RIGHT; + } + if (JOYP & Joypad.MODE_BUTTONS) { + if (this.b) JOYP |= Joypad.B; + if (this.a) JOYP |= Joypad.A; + if (this.start) JOYP |= Joypad.START; + if (this.select) JOYP |= Joypad.SELECT; + } + this.cpu.ram.set(Mem.JOYP, ~JOYP & 0x3f); + } + + onClose() { + throw new Quit(); + } + + onKeyDown(event: sdl.KeyDown) { + let need_interrupt = true; + switch (event.key) { + case "escape": + throw new Quit(); + case "shift": + this.turbo = true; + need_interrupt = false; + break; + case "up": + this.up = true; + break; + case "down": + this.down = true; + break; + case "left": + this.left = true; + break; + case "right": + this.right = true; + break; + case "z": + this.b = true; + break; + case "x": + this.a = true; + break; + case "\n": + this.start = true; + break; + case " ": + this.select = true; + break; + default: + need_interrupt = false; + break; + } + if (need_interrupt) { + this.cpu.stop = false; + this.cpu.interrupt(Interrupt.JOYPAD); + } + } + + onKeyUp(event: sdl.KeyUp) { + switch (event.key) { + case "shift": + this.turbo = false; + break; + case "up": + this.up = false; + break; + case "down": + this.down = false; + break; + case "left": + this.left = false; + break; + case "right": + this.right = false; + break; + case "z": + this.b = false; + break; + case "x": + this.a = false; + break; + case "\n": + this.start = false; + break; + case " ": + this.select = false; + break; + } + } +} diff --git a/js/src/cart.ts b/js/src/cart.ts new file mode 100644 index 0000000..8e5ab1d --- /dev/null +++ b/js/src/cart.ts @@ -0,0 +1,105 @@ +import { HeaderChecksumFailed, LogoChecksumFailed } from "./errors"; + +var fs = require("fs"); + +enum CartType { + RomOnly = 0x00, + RomMbc1 = 0x01, + RomMbc1Ram = 0x02, + RomMbc1RamBatt = 0x03, + RomMbc2 = 0x05, + RomMbc2Batt = 0x06, + RomRam = 0x08, + RomRamBatt = 0x09, + RomMmm01 = 0x0b, + RomMmm01Sram = 0x0c, + RomMmm01SramBatt = 0x0d, + RomMbc3TimerBatt = 0x0f, + RomMbc3TimerRamBatt = 0x10, + RomMbc3 = 0x11, + RomMbc3Ram = 0x12, + RomMbc3RamBatt = 0x13, + RomMbc5 = 0x19, + RomMbc5Ram = 0x1a, + RomMbc5RamBatt = 0x1b, + RomMbc5Rumble = 0x1c, + RomMbc5RumbleRam = 0x1d, + RomMbc5RumbleRamBatt = 0x1e, + PocketCamera = 0x1f, + BandaiTama5 = 0xfd, + HudsonHuc3 = 0xfe, + HudsonHuc1 = 0xff, +} + +const KB: number = 1024; + +function parse_rom_size(val: u8): number { + return (32 * KB) << val; +} + +function parse_ram_size(val: u8): number { + return ( + { + 0: 0, + 2: 8 * KB, + 3: 32 * KB, + 4: 128 * KB, + 5: 64 * KB, + }[val] || 0 + ); +} + +export class Cart { + data: Uint8Array; + ram: Uint8Array; + + logo: Uint8Array; + name: string; + is_gbc: boolean; + licensee: number; + is_sgb: boolean; + cart_type: CartType; + rom_size: number; + ram_size: number; + destination: number; + old_licensee: number; + rom_version: number; + complement_check: number; + checksum: number; + + constructor(rom: string) { + this.data = new Uint8Array(fs.readFileSync(rom, null)); + this.ram = new Uint8Array(0); + + this.logo = this.data.slice(0x0104, 0x0104 + 48); + this.name = Buffer.from(this.data.slice(0x0134, 0x0134 + 16)) + .toString() + .trim(); + this.is_gbc = this.data[0x143] == 0x80; // 0x80 = works on both, 0xC0 = colour only + this.licensee = (this.data[0x144] << 8) | this.data[0x145]; + this.is_sgb = this.data[0x146] == 0x03; + this.cart_type = CartType.RomOnly; // FIXME val to enum + this.rom_size = parse_rom_size(this.data[0x148]); + this.ram_size = parse_ram_size(this.data[0x149]); + this.destination = this.data[0x14a]; + this.old_licensee = this.data[0x14b]; + this.rom_version = this.data[0x14c]; + this.complement_check = this.data[0x14d]; + this.checksum = (this.data[0x14e] << 8) | this.data[0x14f]; + + var logo_checksum = this.logo.reduce((a, b) => a + b, 0) & 0xffff; + if (logo_checksum != 5446) { + throw new LogoChecksumFailed(logo_checksum); + } + + var header_checksum = + this.data.slice(0x0134, 0x014e).reduce((a, b) => a + b, 25) & 0xff; + if (header_checksum != 0) { + throw new HeaderChecksumFailed(header_checksum); + } + + if (this.ram_size) { + // FIXME + } + } +} diff --git a/js/src/clock.ts b/js/src/clock.ts new file mode 100644 index 0000000..f0094f6 --- /dev/null +++ b/js/src/clock.ts @@ -0,0 +1,64 @@ +import { Buttons } from "./buttons"; +import { Timeout } from "./errors"; + +let sleep = require("util").promisify(setTimeout); + +export class Clock { + buttons: Buttons; + frames: number; + profile: number; + turbo: boolean; + + cycle: number; + frame: number; + last_frame_start: number; + start: number; + + constructor( + buttons: Buttons, + frames: number, + profile: number, + turbo: boolean, + ) { + this.buttons = buttons; + this.frames = frames; + this.profile = profile; + this.turbo = turbo; + + this.cycle = 0; + this.frame = 0; + this.last_frame_start = Date.now(); + this.start = Date.now(); + } + + async tick() { + this.cycle += 1; + + // Do a whole frame's worth of sleeping at the start of each frame + if (this.cycle % 17556 == 20) { + // Sleep if we have time left over + let time_spent = Date.now() - this.last_frame_start; + let time_per_frame = 1000.0 / 60.0; + if ( + !this.turbo && + !this.buttons.turbo && + time_spent < time_per_frame + ) { + let sleep_time = time_per_frame - time_spent; + await sleep(sleep_time); + } + this.last_frame_start = Date.now(); + + // Exit if we've hit the frame or time limit + let duration = this.last_frame_start - this.start; + if ( + (this.frames && this.frame >= this.frames) || + (this.profile && duration >= this.profile * 1000) + ) { + throw new Timeout(this.frame, duration / 1000); + } + + this.frame += 1; + } + } +} diff --git a/js/src/consts.ts b/js/src/consts.ts new file mode 100644 index 0000000..e17b060 --- /dev/null +++ b/js/src/consts.ts @@ -0,0 +1,77 @@ +export enum Mem { + VBlankHandler = 0x40, + LcdHandler = 0x48, + TimerHandler = 0x50, + SerialHandler = 0x58, + JoypadHandler = 0x60, + + TileData = 0x8000, + Map0 = 0x9800, + Map1 = 0x9c00, + OamBase = 0xfe00, + + JOYP = 0xff00, + + _SB = 0xff01, // Serial Data + _SC = 0xff02, // Serial Control + + DIV = 0xff04, + TIMA = 0xff05, + TMA = 0xff06, + TAC = 0xff07, + + IF = 0xff0f, + + NR10 = 0xff10, + /* + NR11 = 0xFF11, + NR12 = 0xFF12, + NR13 = 0xFF13, + NR14 = 0xFF14, + + NR20 = 0xFF15, + NR21 = 0xFF16, + NR22 = 0xFF17, + NR23 = 0xFF18, + NR24 = 0xFF19, + + NR30 = 0xFF1A, + NR31 = 0xFF1B, + NR32 = 0xFF1C, + NR33 = 0xFF1D, + NR34 = 0xFF1E, + + NR40 = 0xFF1F, + NR41 = 0xFF20, + NR42 = 0xFF21, + NR43 = 0xFF22, + NR44 = 0xFF23, + + NR50 = 0xFF24, + NR51 = 0xFF25, + NR52 = 0xFF26, + */ + LCDC = 0xff40, + STAT = 0xff41, + SCY = 0xff42, // SCROLL_Y + SCX = 0xff43, // SCROLL_X + LY = 0xff44, // LY aka currently drawn line, 0-153, >144 = vblank + LYC = 0xff45, + DMA = 0xff46, + BGP = 0xff47, + OBP0 = 0xff48, + OBP1 = 0xff49, + WY = 0xff4a, + WX = 0xff4b, + BOOT = 0xff50, + + IE = 0xffff, +} + +export enum Interrupt { + VBLANK = 1 << 0, + STAT = 1 << 1, + TIMER = 1 << 2, + SERIAL = 1 << 3, + JOYPAD = 1 << 4, +} diff --git a/js/src/cpu.ts b/js/src/cpu.ts new file mode 100644 index 0000000..03b3da3 --- /dev/null +++ b/js/src/cpu.ts @@ -0,0 +1,1361 @@ +import { Interrupt } from "./consts"; +import { RAM } from "./ram"; +import { Mem } from "./consts"; +import { InvalidOpcode, UnitTestFailed, UnitTestPassed } from "./errors"; +import { hex } from "./_utils"; + +// prettier-ignore +const OP_CYCLES = [ + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, + 0, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 2, 2, 2, 2, 2, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 0, 3, 6, 2, 4, + 2, 3, 3, 0, 3, 4, 2, 4, 2, 4, 3, 0, 3, 0, 2, 4, + 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, + 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4, +]; + +// prettier-ignore +const OP_CB_CYCLES = [ + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, +]; + +// prettier-ignore +const OP_ARG_TYPES = [ + //1 2 3 4 5 6 7 8 9 A B C D E F + 0, 2, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, // 0 + 1, 2, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 1, 0, // 1 + 3, 2, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 1, 0, // 2 + 3, 2, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 1, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 2, 2, 2, 0, 1, 0, 0, 0, 2, 0, 2, 2, 1, 0, // C + 0, 0, 2, 0, 2, 0, 1, 0, 0, 0, 2, 0, 2, 0, 1, 0, // D + 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 1, 0, // E + 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 1, 0, // F +]; + +const OP_ARG_BYTES = [0, 1, 2, 1]; + +// prettier-ignore +const OP_NAMES = [ + "NOP", "LD BC,$u16", "LD [BC],A", "INC BC", "INC B", "DEC B", "LD B,$u8", "RCLA", + "LD [$u16],SP", "ADD HL,BC", "LD A,[BC]", "DEC BC", "INC C", "DEC C", "LD C,$u8", "RRCA", + "STOP", "LD DE,$u16", "LD [DE],A", "INC DE", "INC D", "DEC D", "LD D,$u8", "RLA", + "JR i8", "ADD HL,DE", "LD A,[DE]", "DEC DE", "INC E", "DEC E", "LD E,$u8", "RRA", + "JR NZ,i8", "LD HL,$u16", "LD [HL+],A", "INC HL", "INC H", "DEC H", "LD H,$u8", "DAA", + "JR Z,i8", "ADD HL,HL", "LD A,[HL+]", "DEC HL", "INC L", "DEC L", "LD L,$u8", "CPL", + "JR NC,i8", "LD SP,$u16", "LD [HL-],A", "INC SP", "INC [HL]", "DEC [HL]", "LD [HL],$u8", "SCF", + "JR C,i8", "ADD HL,SP", "LD A,[HL-]", "DEC SP", "INC A", "DEC A", "LD A,$u8", "CCF", + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,[HL]", "LD B,A", + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,[HL]", "LD C,A", + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,[HL]", "LD D,A", + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,[HL]", "LD E,A", + "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,[HL]", "LD H,A", + "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,[HL]", "LD L,A", + "LD [HL],B", "LD [HL],C", "LD [HL],D", "LD [HL],E", "LD [HL],H", "LD [HL],L", "HALT", "LD [HL],A", + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,[HL]", "LD A,A", + "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,H", "ADD A,L", "ADD A,[HL]", "ADD A,A", + "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,H", "ADC A,L", "ADC A,[HL]", "ADC A,A", + "SUB A,B", "SUB A,C", "SUB A,D", "SUB A,E", "SUB A,H", "SUB A,L", "SUB A,[HL]", "SUB A,A", + "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,H", "SBC A,L", "SBC A,[HL]", "SBC A,A", + "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND [HL]", "AND A", + "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR [HL]", "XOR A", + "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR [HL]", "OR A", + "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP [HL]", "CP A", + "RET NZ", "POP BC", "JP NZ,$u16", "JP $u16", "CALL NZ,$u16", "PUSH BC", "ADD A,$u8", "RST 00", + "RET Z", "RET", "JP Z,$u16", "ERR CB", "CALL Z,$u16", "CALL $u16", "ADC A,$u8", "RST 08", + "RET NC", "POP DE", "JP NC,$u16", "ERR D3", "CALL NC,$u16", "PUSH DE", "SUB A,$u8", "RST 10", + "RET C", "RETI", "JP C,$u16", "ERR DB", "CALL C,$u16", "ERR DD", "SBC A,$u8", "RST 18", + "LDH [$u8],A", "POP HL", "LDH [C],A", "DBG", "ERR E4", "PUSH HL", "AND $u8", "RST 20", + "ADD SP i8", "JP HL", "LD [$u16],A", "ERR EB", "ERR EC", "ERR ED", "XOR $u8", "RST 28", + "LDH A,[$u8]", "POP AF", "LDH A,[C]", "DI", "ERR F4", "PUSH AF", "OR $u8", "RST 30", + "LD HL,SPi8", "LD SP,HL", "LD A,[$u16]", "EI", "ERR FC", "ERR FD", "CP $u8", "RST 38", +]; + +// prettier-ignore +const CB_OP_NAMES = [ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC [HL]", "RLC A", + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC [HL]", "RRC A", + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL [HL]", "RL A", + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR [HL]", "RR A", + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA [HL]", "SLA A", + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA [HL]", "SRA A", + "SWAP B", "SWAP C", "SWAP D", "SWAP E", "SWAP H", "SWAP L", "SWAP [HL]", "SWAP A", + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL [HL]", "SRL A", + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,[HL]", "BIT 0,A", + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,[HL]", "BIT 1,A", + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,[HL]", "BIT 2,A", + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,[HL]", "BIT 3,A", + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,[HL]", "BIT 4,A", + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,[HL]", "BIT 5,A", + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,[HL]", "BIT 6,A", + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,[HL]", "BIT 7,A", + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,[HL]", "RES 0,A", + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,[HL]", "RES 1,A", + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,[HL]", "RES 2,A", + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,[HL]", "RES 3,A", + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,[HL]", "RES 4,A", + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,[HL]", "RES 5,A", + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,[HL]", "RES 6,A", + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,[HL]", "RES 7,A", + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,[HL]", "SET 0,A", + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,[HL]", "SET 1,A", + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,[HL]", "SET 2,A", + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,[HL]", "SET 3,A", + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,[HL]", "SET 4,A", + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,[HL]", "SET 5,A", + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,[HL]", "SET 6,A", + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,[HL]", "SET 7,A", +]; + +class oparg { + // FIXME should be a union + as_u8: u8 = 0; + as_i8: i8 = 0; + as_u16: u16 = 0; +} + +function int8(val: number): i8 { + val = val & 0xff; + if (val > 127) { + val -= 0x100; + } + return val; +} + +function uint8(val: number): u8 { + return val & 0xff; +} + +function uint16(val: number): u16 { + return val & 0xffff; +} + +function split16(val: number): [u8, u8] { + return [uint8(val >> 8), uint8(val & 0xff)]; +} + +function join16(a: u8, b: u8): u16 { + return (uint16(a) << 8) | uint16(b); +} + +export class CPU { + ram: RAM; + debug: boolean; + stop: boolean; + halt: boolean; + + flag_c: boolean; + flag_h: boolean; + flag_n: boolean; + flag_z: boolean; + pc: u16; + sp: u16; + hl: u16; + f: u8; + e: u8; + d: u8; + c: u8; + b: u8; + a: u8; + cycle: number; + interrupts: boolean; + owed_cycles: number; + + constructor(ram: RAM, debug: boolean) { + this.ram = ram; + this.debug = debug; + this.stop = false; + + this.stop = false; + this.halt = false; + this.owed_cycles = 0; + this.interrupts = false; + this.cycle = 0; + + // FIXME: these should be unioned + // A, B, C, D, E, H, L, F uint8 + // AF, BC, DE, HL, SP, PC uint16 + this.a = 0; + this.b = 0; + this.c = 0; + this.d = 0; + this.e = 0; + this.f = 0; + this.hl = 0; + this.sp = 0; + this.pc = 0; + this.flag_z = false; + this.flag_n = false; + this.flag_h = false; + this.flag_c = false; + } + + tick(): void { + this.tick_dma(); + this.tick_clock(); + this.tick_interrupts(); + if (this.halt) { + return; + } + if (this.stop) { + return; + } + this.tick_instructions(); + } + + interrupt(i: Interrupt): void { + // Set a given interrupt bit - on the next tick, if the interrupt + // handler for self interrupt is enabled (and interrupts in general + // are enabled), then the interrupt handler will be called. + this.ram.set(Mem.IF, this.ram.get(Mem.IF) | i); + this.halt = false; // interrupts interrupt HALT state + } + + dump_regs(): void { + // stack + const sp_val = + this.ram.get(this.sp) | (this.ram.get((this.sp + 1) & 0xffff) << 8); + + // interrupts + const IE = this.ram.get(Mem.IE); + const IF = this.ram.get(Mem.IF); + const z = (this.f >> 7) & 1 ? "Z" : "z"; + const n = (this.f >> 6) & 1 ? "N" : "n"; + const h = (this.f >> 5) & 1 ? "H" : "h"; + const c = (this.f >> 4) & 1 ? "C" : "c"; + const v = (IE >> 0) & 1 ? ((IF >> 0) & 1 ? "V" : "v") : "_"; + const l = (IE >> 1) & 1 ? ((IF >> 1) & 1 ? "L" : "l") : "_"; + const t = (IE >> 2) & 1 ? ((IF >> 2) & 1 ? "T" : "t") : "_"; + const s = (IE >> 3) & 1 ? ((IF >> 3) & 1 ? "S" : "s") : "_"; + const j = (IE >> 4) & 1 ? ((IF >> 4) & 1 ? "J" : "j") : "_"; + + // opcode & args + let op = this.ram.get(this.pc); + let op_str = ""; + if (op == 0xcb) { + op = this.ram.get(this.pc + 1); + op_str = CB_OP_NAMES[op]; + } else { + switch (OP_ARG_TYPES[op]) { + case 0: + op_str = OP_NAMES[op]; + break; + case 1: + op_str = OP_NAMES[op].replace( + "u8", + hex(this.ram.get(this.pc + 1), 2), + ); + break; + case 2: + op_str = OP_NAMES[op].replace( + "u16", + hex( + uint16(this.ram.get(this.pc + 1)) | + (uint16(this.ram.get(this.pc + 2)) << 8), + 4, + ), + ); + break; + case 3: + op_str = OP_NAMES[op].replace( + "i8", + int8(this.ram.get(this.pc + 1)).toString(), + ); + break; + } + } + + // print + console.log( + `${hex(this.a, 2)}${hex(this.f, 2)} ` + + `${hex(this.b, 2)}${hex(this.c, 2)} ` + + `${hex(this.d, 2)}${hex(this.e, 2)} ` + + `${hex(this.hl, 4)} : ${hex(this.sp, 4)} = ${hex(sp_val, 4)} ` + + `: ${z}${n}${h}${c} : ${v}${l}${t}${s}${j} ` + + `: ${hex(this.pc, 4)} = ${hex(op, 2)} : ${op_str}`, + ); + } + + tick_dma(): void { + // TODO: DMA should take 26 cycles, during which main RAM is inaccessible + if (this.ram.get(Mem.DMA) > 0) { + const dma_src = uint16(this.ram.get(Mem.DMA)) << 8; + for (let i = 0; i < 0x60; i++) { + this.ram.set( + uint16(Mem.OamBase + i), + this.ram.get(dma_src + uint16(i)), + ); + } + this.ram.set(Mem.DMA, 0x00); + } + } + + tick_clock(): void { + this.cycle++; + + // TODO: writing any value to Mem.DIV should reset it to 0x00 + // increment at 16384Hz (each 64 cycles?) + if (this.cycle % 64 == 0) { + this.ram.set(Mem.DIV, (this.ram.get(Mem.DIV) + 1) & 0xff); + } + + if ((this.ram.get(Mem.TAC) & (1 << 2)) > 0) { + // timer enable + const speeds = [256, 4, 16, 64]; // increment per X cycles + const speed = speeds[this.ram.get(Mem.TAC) & 0x03]; + if (this.cycle % speed == 0) { + if (this.ram.get(Mem.TIMA) == 0xff) { + this.ram.set(Mem.TIMA, this.ram.get(Mem.TMA)); // if timer overflows, load base + this.interrupt(Interrupt.TIMER); + } + this.ram.set(Mem.TIMA, (this.ram.get(Mem.TIMA) + 1) & 0xff); + } + } + } + + check_interrupt(queue: u8, i: Interrupt, handler: u16): boolean { + if ((queue & i) > 0) { + // TODO: wait two cycles + // TODO: push16(PC) should also take two cycles + // TODO: one more cycle to store new PC + this.push(this.pc); + this.pc = handler; + this.ram.set(Mem.IF, this.ram.get(Mem.IF) & ~i); + return true; + } + return false; + } + + tick_interrupts(): void { + const queue = this.ram.get(Mem.IE) & this.ram.get(Mem.IF); + if (this.interrupts && queue != 0x00) { + if (this.debug) { + console.log( + `Handling interrupts: ${hex( + this.ram.get(Mem.IE), + 2, + )} & ${hex(this.ram.get(Mem.IF), 2)}`, + ); + } + this.interrupts = false; // no nested interrupts, RETI will re-enable + this.check_interrupt(queue, Interrupt.VBLANK, Mem.VBlankHandler) || + this.check_interrupt(queue, Interrupt.STAT, Mem.LcdHandler) || + this.check_interrupt( + queue, + Interrupt.TIMER, + Mem.TimerHandler, + ) || + this.check_interrupt( + queue, + Interrupt.SERIAL, + Mem.SerialHandler, + ) || + this.check_interrupt( + queue, + Interrupt.JOYPAD, + Mem.JoypadHandler, + ); + } + } + + tick_instructions(): void { + // if the previous instruction was large, let's not run any + // more instructions until other subsystems have caught up + if (this.owed_cycles > 0) { + this.owed_cycles--; + return; + } + + if (this.debug) { + this.dump_regs(); + } + + let op = this.ram.get(this.pc); + if (op == 0xcb) { + op = this.ram.get(this.pc + 1); + this.pc += 2; + this.tick_cb(op); + this.owed_cycles = OP_CB_CYCLES[op] - 1; + } else { + let arg = new oparg(); + let arg_len = OP_ARG_BYTES[OP_ARG_TYPES[op]]; + if (arg_len == 1) { + arg.as_u8 = this.ram.get(this.pc + 1); + arg.as_i8 = int8(arg.as_u8); + } + if (arg_len == 2) { + const low = this.ram.get(this.pc + 1); + const high = this.ram.get(this.pc + 2); + arg.as_u16 = uint16(low) | (uint16(high) << 8); + } + this.pc += 1 + arg_len; + + this.tick_main(op, arg); + this.owed_cycles = OP_CYCLES[op] - 1; + } + + // Flags should be union'ed with the F register, but php doesn't + // support that, so let's manually sync from flags to register + // after every instruction... + this.f = 0; + if (this.flag_z) { + this.f |= 1 << 7; + } + if (this.flag_n) { + this.f |= 1 << 6; + } + if (this.flag_h) { + this.f |= 1 << 5; + } + if (this.flag_c) { + this.f |= 1 << 4; + } + + // HALT has cycles=0 + if (this.owed_cycles < 0) { + this.owed_cycles = 0; + } + } + + tick_main(op: u8, arg: oparg): void { + // Execute + switch (op) { + case 0x00 /* NOP */: + break; + case 0x01: + [this.b, this.c] = split16(arg.as_u16); + break; + case 0x02: + this.ram.set(join16(this.b, this.c), this.a); + break; + case 0x03: + [this.b, this.c] = split16(join16(this.b, this.c) + 1); + break; + case 0x08: + this.ram.set(arg.as_u16 + 1, uint8((this.sp >> 8) & 0xff)); + this.ram.set(arg.as_u16, uint8(this.sp & 0xff)); + break; + case 0x0a: + this.a = this.ram.get(join16(this.b, this.c)); + break; + case 0x0b: + [this.b, this.c] = split16(join16(this.b, this.c) - 1); + break; + case 0x10: + this.stop = true; + break; + case 0x11: + [this.d, this.e] = split16(arg.as_u16); + break; + case 0x12: + this.ram.set(join16(this.d, this.e), this.a); + break; + case 0x13: + [this.d, this.e] = split16(join16(this.d, this.e) + 1); + break; + case 0x18: + this.pc += arg.as_i8; + break; + case 0x1a: + this.a = this.ram.get(join16(this.d, this.e)); + break; + case 0x1b: + [this.d, this.e] = split16(join16(this.d, this.e) - 1); + break; + + case 0x20: + if (!this.flag_z) { + this.pc += arg.as_i8; + } + break; + case 0x21: + this.hl = arg.as_u16; + break; + case 0x22: + this.ram.set(this.hl, this.a); + this.hl = (this.hl + 1) & 0xffff; + break; + case 0x23: + this.hl = (this.hl + 1) & 0xffff; + break; + case 0x27: + var val16 = uint16(this.a); + if (!this.flag_n) { + if (this.flag_h || (val16 & 0x0f) > 9) { + val16 += 6; + } + if (this.flag_c || val16 > 0x9f) { + val16 += 0x60; + } + } else { + if (this.flag_h) { + val16 -= 6; + if (!this.flag_c) { + val16 &= 0xff; + } + } + if (this.flag_c) { + val16 -= 0x60; + } + } + this.flag_h = false; + if ((val16 & 0x100) > 0) { + this.flag_c = true; + } + this.a = uint8(val16 & 0xff); + this.flag_z = this.a == 0; + break; + case 0x28: + if (this.flag_z) { + this.pc += arg.as_i8; + } + break; + case 0x2a: + this.a = this.ram.get(this.hl); + this.hl = (this.hl + 1) & 0xffff; + break; + case 0x2b: + this.hl = (this.hl - 1) & 0xffff; + break; + case 0x2f: + this.a ^= 0xff; + this.flag_n = true; + this.flag_h = true; + break; + case 0x30: + if (!this.flag_c) { + this.pc += arg.as_i8; + } + break; + case 0x31: + this.sp = arg.as_u16; + break; + case 0x32: + this.ram.set(this.hl, this.a); + this.hl = (this.hl - 1) & 0xffff; + break; + case 0x33: + this.sp = (this.sp + 1) & 0xffff; + break; + case 0x37: + this.flag_n = false; + this.flag_h = false; + this.flag_c = true; + break; + case 0x38: + if (this.flag_c) { + this.pc += arg.as_i8; + } + break; + case 0x3a: + this.a = this.ram.get(this.hl); + this.hl = (this.hl - 1) & 0xffff; + break; + case 0x3b: + this.sp = (this.sp - 1) & 0xffff; + break; + case 0x3f: + this.flag_c = !this.flag_c; + this.flag_n = false; + this.flag_h = false; + break; + + // INC r + case 0x04: + case 0x0c: + case 0x14: + case 0x1c: + case 0x24: + case 0x2c: + case 0x34: + case 0x3c: + var val = this.get_reg((op - 0x04) / 8); + this.flag_h = (val & 0x0f) == 0x0f; + val = (val + 1) & 0xff; + this.flag_z = val == 0; + this.flag_n = false; + this.set_reg((op - 0x04) / 8, val); + break; + + // DEC r + case 0x05: + case 0x0d: + case 0x15: + case 0x1d: + case 0x25: + case 0x2d: + case 0x35: + case 0x3d: + var val = this.get_reg((op - 0x05) / 8); + val = (val - 1) & 0xff; + this.flag_h = (val & 0x0f) == 0x0f; + this.flag_z = val == 0; + this.flag_n = true; + this.set_reg((op - 0x05) / 8, val); + break; + + // LD r,n + case 0x06: + case 0x0e: + case 0x16: + case 0x1e: + case 0x26: + case 0x2e: + case 0x36: + case 0x3e: + this.set_reg((op - 0x06) / 8, arg.as_u8); + break; + + // RCLA, RLA, RRCA, RRA + case 0x07: + case 0x17: + case 0x0f: + case 0x1f: + const carry = this.flag_c ? 1 : 0; + if (op == 0x07) { + // RCLA + this.flag_c = (this.a & (1 << 7)) != 0; + this.a = (this.a << 1) | (this.a >> 7); + } + if (op == 0x17) { + // RLA + this.flag_c = (this.a & (1 << 7)) != 0; + this.a = (this.a << 1) | carry; + } + if (op == 0x0f) { + // RRCA + this.flag_c = (this.a & (1 << 0)) != 0; + this.a = (this.a >> 1) | (this.a << 7); + } + if (op == 0x1f) { + // RRA + this.flag_c = (this.a & (1 << 0)) != 0; + this.a = (this.a >> 1) | (carry << 7); + } + this.a &= 0xff; + this.flag_n = false; + this.flag_h = false; + this.flag_z = false; + break; + + // ADD HL,rr + case 0x09: + case 0x19: + case 0x29: + case 0x39: + var val16 = -1; + if (op == 0x09) { + val16 = join16(this.b, this.c); + } + if (op == 0x19) { + val16 = join16(this.d, this.e); + } + if (op == 0x29) { + val16 = this.hl; + } + if (op == 0x39) { + val16 = this.sp; + } + this.flag_h = (this.hl & 0x0fff) + (val16 & 0x0fff) > 0x0fff; + this.flag_c = this.hl + val16 > 0xffff; + this.hl = (this.hl + val16) & 0xffff; + this.flag_n = false; + break; + + // LD r,r + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + if (op == 0x76) { + // FIXME: weird timing side effects + this.halt = true; + break; + } + this.set_reg((op - 0x40) >> 3, this.get_reg(op - 0x40)); + break; + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + this._add(this.get_reg(op)); + break; + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + this._adc(this.get_reg(op)); + break; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + this._sub(this.get_reg(op)); + break; + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + this._sbc(this.get_reg(op)); + break; + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + this._and(this.get_reg(op)); + break; + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + this._xor(this.get_reg(op)); + break; + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + this._or(this.get_reg(op)); + break; + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + this._cp(this.get_reg(op)); + break; + + case 0xc0: + if (!this.flag_z) { + this.pc = this.pop(); + } + break; + case 0xc1: + [this.b, this.c] = split16(this.pop()); + break; + case 0xc2: + if (!this.flag_z) { + this.pc = arg.as_u16; + } + break; + case 0xc3: + this.pc = arg.as_u16; + break; + case 0xc4: + if (!this.flag_z) { + this.push(this.pc); + this.pc = arg.as_u16; + } + break; + case 0xc5: + this.push(join16(this.b, this.c)); + break; + case 0xc6: + this._add(arg.as_u8); + break; + case 0xc7: + this.push(this.pc); + this.pc = 0x00; + break; + case 0xc8: + if (this.flag_z) { + this.pc = this.pop(); + } + break; + case 0xc9: + this.pc = this.pop(); + break; + case 0xca: + if (this.flag_z) { + this.pc = arg.as_u16; + } + break; + // case 0xCB: break; + case 0xcc: + if (this.flag_z) { + this.push(this.pc); + this.pc = arg.as_u16; + } + break; + case 0xcd: + this.push(this.pc); + this.pc = arg.as_u16; + break; + case 0xce: + this._adc(arg.as_u8); + break; + case 0xcf: + this.push(this.pc); + this.pc = 0x08; + break; + + case 0xd0: + if (!this.flag_c) { + this.pc = this.pop(); + } + break; + case 0xd1: + [this.d, this.e] = split16(this.pop()); + break; + case 0xd2: + if (!this.flag_c) { + this.pc = arg.as_u16; + } + break; + // case 0xD3: break; + case 0xd4: + if (!this.flag_c) { + this.push(this.pc); + this.pc = arg.as_u16; + } + break; + case 0xd5: + this.push(join16(this.d, this.e)); + break; + case 0xd6: + this._sub(arg.as_u8); + break; + case 0xd7: + this.push(this.pc); + this.pc = 0x10; + break; + case 0xd8: + if (this.flag_c) { + this.pc = this.pop(); + } + break; + case 0xd9: + this.pc = this.pop(); + this.interrupts = true; + break; + case 0xda: + if (this.flag_c) { + this.pc = arg.as_u16; + } + break; + // case 0xDB: break; + case 0xdc: + if (this.flag_c) { + this.push(this.pc); + this.pc = arg.as_u16; + } + break; + // case 0xDD: break; + case 0xde: + this._sbc(arg.as_u8); + break; + case 0xdf: + this.push(this.pc); + this.pc = 0x18; + break; + case 0xe0: + this.ram.set(0xff00 + uint16(arg.as_u8), this.a); + if (arg.as_u8 == 0x01) { + process.stdout.write(String.fromCharCode(this.a)); + } + break; + case 0xe1: + this.hl = this.pop(); + break; + case 0xe2: + this.ram.set(0xff00 + uint16(this.c), this.a); + if (this.c == 0x01) { + process.stdout.write(String.fromCharCode(this.a)); + } + break; + // case 0xE3: break; + // case 0xE4: break; + case 0xe5: + this.push(this.hl); + break; + case 0xe6: + this._and(arg.as_u8); + break; + case 0xe7: + this.push(this.pc); + this.pc = 0x20; + break; + case 0xe8: + val16 = (this.sp + arg.as_i8) & 0xffff; + //this.flag_h = ((this.sp & 0x0FFF) + (arg.as_i8 & 0x0FFF) > 0x0FFF); + //this.flag_c = (this.sp + arg.as_i8 > 0xFFFF); + this.flag_h = + ((this.sp ^ uint16(arg.as_i8) ^ val16) & 0x10) > 0; + this.flag_c = + ((this.sp ^ uint16(arg.as_i8) ^ val16) & 0x100) > 0; + this.sp = (this.sp + arg.as_i8) & 0xffff; + this.flag_z = false; + this.flag_n = false; + break; + case 0xe9: + this.pc = this.hl; + break; + case 0xea: + this.ram.set(arg.as_u16, this.a); + break; + // case 0xEB: break; + // case 0xEC: break; + // case 0xED: break; + case 0xee: + this._xor(arg.as_u8); + break; + case 0xef: + this.push(this.pc); + this.pc = 0x28; + break; + + case 0xf0: + this.a = this.ram.get(0xff00 + uint16(arg.as_u8)); + break; + case 0xf1: + [this.a, this.f] = split16(this.pop() & 0xfff0); + this.flag_z = (this.f & (1 << 7)) > 0; + this.flag_n = (this.f & (1 << 6)) > 0; + this.flag_h = (this.f & (1 << 5)) > 0; + this.flag_c = (this.f & (1 << 4)) > 0; + break; + case 0xf2: + this.a = this.ram.get(0xff00 + uint16(this.c)); + break; + case 0xf3: + this.interrupts = false; + break; + // case 0xF4: break; + case 0xf5: + this.push(join16(this.a, this.f)); + break; + case 0xf6: + this._or(arg.as_u8); + break; + case 0xf7: + this.push(this.pc); + this.pc = 0x30; + break; + case 0xf8: + if (arg.as_i8 >= 0) { + this.flag_c = (this.sp & 0xff) + (arg.as_i8 & 0xff) > 0xff; + this.flag_h = (this.sp & 0x0f) + (arg.as_i8 & 0x0f) > 0x0f; + } else { + this.flag_c = + uint8((this.sp + arg.as_i8) & 0xff) <= + uint8(this.sp & 0xff); + this.flag_h = + uint8((this.sp + arg.as_i8) & 0x0f) <= + uint8(this.sp & 0x0f); + } + // this.flag_h = ((((this.sp & 0x0f) + (arg.as_u8 & 0x0f)) & 0x10) != 0); + // this.flag_c = ((((this.sp & 0xff) + (arg.as_u8 & 0xff)) & 0x100) != 0); + this.hl = (this.sp + arg.as_i8) & 0xffff; + this.flag_z = false; + this.flag_n = false; + break; + case 0xf9: + this.sp = this.hl; + break; + case 0xfa: + this.a = this.ram.get(arg.as_u16); + break; + case 0xfb: + this.interrupts = true; + break; + case 0xfc: + throw new UnitTestPassed(); + case 0xfd: + throw new UnitTestFailed(); + case 0xfe: + this._cp(arg.as_u8); + break; + case 0xff: + this.push(this.pc); + this.pc = 0x38; + break; + + // missing ops + default: + throw new InvalidOpcode(op); + } + } + + tick_cb(op: u8) { + var val = this.get_reg(op); + switch (true) { + // RLC + case op <= 0x07: + this.flag_c = (val & (1 << 7)) != 0; + val <<= 1; + val &= 0xff; + if (this.flag_c) { + val |= 1 << 0; + } + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // RRC + case op <= 0x0f: + this.flag_c = (val & (1 << 0)) != 0; + val >>= 1; + if (this.flag_c) { + val |= 1 << 7; + } + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // RL + case op <= 0x17: + var orig_c = this.flag_c; + this.flag_c = (val & (1 << 7)) != 0; + val <<= 1; + val &= 0xff; + if (orig_c) { + val |= 1 << 0; + } + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // RR + case op <= 0x1f: + var orig_c = this.flag_c; + this.flag_c = (val & (1 << 0)) != 0; + val >>= 1; + if (orig_c) { + val |= 1 << 7; + } + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // SLA + case op <= 0x27: + this.flag_c = (val & (1 << 7)) != 0; + val <<= 1; + val &= 0xff; + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // SRA + case op <= 0x2f: + this.flag_c = (val & (1 << 0)) != 0; + val >>= 1; + if ((val & (1 << 6)) > 0) { + val |= 1 << 7; + } + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // SWAP + case op <= 0x37: + val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); + this.flag_c = false; + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // SRL + case op <= 0x3f: + this.flag_c = (val & (1 << 0)) != 0; + val >>= 1; + this.flag_n = false; + this.flag_h = false; + this.flag_z = val == 0; + break; + + // BIT + case op <= 0x7f: + var bit = (op & 0b00111000) >> 3; + this.flag_z = (val & (1 << bit)) == 0; + this.flag_n = false; + this.flag_h = true; + break; + + // RES + case op <= 0xbf: + var bit = (op & 0b00111000) >> 3; + val &= (1 << bit) ^ 0xff; + break; + + // SET + case op <= 0xff: + var bit = (op & 0b00111000) >> 3; + val |= 1 << bit; + break; + + // Should never get here + default: + throw new Error("Unreachable code reached in tick_cb"); + } + this.set_reg(op, val); + } + + _xor(val: u8) { + this.a ^= val; + + this.flag_z = this.a == 0; + this.flag_n = false; + this.flag_h = false; + this.flag_c = false; + } + + _or(val: u8) { + this.a |= val; + + this.flag_z = this.a == 0; + this.flag_n = false; + this.flag_h = false; + this.flag_c = false; + } + + _and(val: u8) { + this.a &= val; + + this.flag_z = this.a == 0; + this.flag_n = false; + this.flag_h = true; + this.flag_c = false; + } + + _cp(val: u8) { + this.flag_z = this.a == val; + this.flag_n = true; + this.flag_h = (this.a & 0x0f) < (val & 0x0f); + this.flag_c = this.a < val; + } + + _add(val: u8) { + this.flag_c = uint16(this.a) + uint16(val) > 0xff; + this.flag_h = (this.a & 0x0f) + (val & 0x0f) > 0x0f; + this.flag_n = false; + this.a = (this.a + val) & 0xff; + this.flag_z = this.a == 0; + } + + _adc(val: u8) { + const carry = this.flag_c ? 1 : 0; + this.flag_c = uint16(this.a) + uint16(val) + uint16(carry) > 0xff; + this.flag_h = (this.a & 0x0f) + (val & 0x0f) + carry > 0x0f; + this.flag_n = false; + this.a = (this.a + val + carry) & 0xff; + this.flag_z = this.a == 0; + } + + _sub(val: u8) { + this.flag_c = this.a < val; + this.flag_h = (this.a & 0x0f) < (val & 0x0f); + this.a = (this.a - val) & 0xff; + this.flag_z = this.a == 0; + this.flag_n = true; + } + + _sbc(val: u8) { + const carry = this.flag_c ? 1 : 0; + const res = this.a - val - carry; + this.flag_h = ((this.a ^ val ^ (uint8(res) & 0xff)) & (1 << 4)) != 0; + this.flag_c = res < 0; + this.a = (this.a - val - carry) & 0xff; + this.flag_z = this.a == 0; + this.flag_n = true; + } + + push(val: u16) { + this.ram.set(this.sp - 1, uint8(((val & 0xff00) >> 8) & 0xff)); + this.ram.set(this.sp - 2, uint8(val & 0xff)); + this.sp -= 2; + } + + pop(): u16 { + var val = + (uint16(this.ram.get(this.sp + 1)) << 8) | + uint16(this.ram.get(this.sp)); + this.sp += 2; + return val; + } + + get_reg(n: u8): u8 { + switch (n & 0x07) { + case 0: + return this.b; + case 1: + return this.c; + case 2: + return this.d; + case 3: + return this.e; + case 4: + return uint8(this.hl >> 8); + case 5: + return uint8(this.hl & 0xff); + case 6: + return this.ram.get(this.hl); + case 7: + return this.a; + default: + throw new Error("Unreachable code reached in get_reg"); + } + } + + set_reg(n: u8, val: u8) { + switch (n & 0x07) { + case 0: + this.b = val; + break; + case 1: + this.c = val; + break; + case 2: + this.d = val; + break; + case 3: + this.e = val; + break; + case 4: + var [_, orig_l] = split16(this.hl); + this.hl = join16(val, orig_l); + break; + case 5: + var [orig_h, _] = split16(this.hl); + this.hl = join16(orig_h, val); + break; + case 6: + this.ram.set(this.hl, val); + break; + case 7: + this.a = val; + break; + default: + throw new Error("Unreachable code reached in set_reg"); + } + } +} diff --git a/js/src/errors.ts b/js/src/errors.ts new file mode 100644 index 0000000..df33379 --- /dev/null +++ b/js/src/errors.ts @@ -0,0 +1,76 @@ +export class EmuError extends Error {} + +export class UnsupportedCart extends EmuError {} +// def __init__(self, cart_type): +// self.cart_type = cart_type + +// Controlled exit, ie we are deliberately stopping emulation +export class ControlledExit extends EmuError {} + +export class Quit extends ControlledExit { + constructor() { + super("User has exited the emulator"); + } +} + +export class Timeout extends ControlledExit { + constructor(frames: number, duration: number) { + let f = frames.toString().padStart(5, " "); + let d = duration.toFixed(2).toString().padStart(5, " "); + let p = (frames / duration).toFixed(2); + super(`Emulated ${f} frames in ${d}s (${p}fps)`); + } +} + +export class UnitTestPassed extends ControlledExit { + constructor() { + super("Unit test passed"); + } +} + +export class UnitTestFailed extends ControlledExit { + constructor() { + super("Unit test failed"); + } +} + +// Game error, ie the game developer has a bug +export class GameException extends EmuError {} + +export class InvalidOpcode extends GameException { + constructor(opcode: u8) { + super(`Invalid opcode ${opcode}`); + } +} + +export class InvalidRamRead extends GameException {} +// def __init__(self, ram_bank, offset, ram_size): +// self.ram_bank = ram_bank +// self.offset = offset +// self.ram_size = ram_size +// return f"Read from RAM bank {self.ram_bank} offset {self.offset} >= ram size {self.ram_size}" + +export class InvalidRamWrite extends GameException {} +// def __init__(self, ram_bank, offset, ram_size): +// return f"Write to RAM bank {self.ram_bank} offset {self.offset} >= ram size {self.ram_size}" + +// User error, ie the user gave us an ivalid or corrupt input file +export class UserException extends EmuError {} + +export class RomMissing extends UserException { + constructor(rom: string, error: Error) { + super(`Error opening ${rom}: ${error}`); + } +} + +export class LogoChecksumFailed extends UserException { + constructor(logo_checksum: u16) { + super(`Logo checksum failed: ${logo_checksum} != 5446`); + } +} + +export class HeaderChecksumFailed extends UserException { + constructor(header_checksum: u8) { + super(`Header checksum failed: ${header_checksum} != 0`); + } +} diff --git a/js/src/gameboy.ts b/js/src/gameboy.ts new file mode 100644 index 0000000..3faa595 --- /dev/null +++ b/js/src/gameboy.ts @@ -0,0 +1,54 @@ +import { Cart } from "./cart"; +import { RAM } from "./ram"; +import { CPU } from "./cpu"; +import { GPU } from "./gpu"; +import { APU } from "./apu"; +import { Buttons } from "./buttons"; +import { Clock } from "./clock"; + +export class GameBoy { + cart: Cart; + ram: RAM; + cpu: CPU; + gpu: GPU; + apu: APU; + buttons: Buttons; + clock: Clock; + + constructor(args: any) { + // let sdl = sdl2::init().map_err(anyhow::Error::msg)?; + let sdl = null; + + this.cart = new Cart(args.rom); + this.ram = new RAM(this.cart, args.debugRam); + this.cpu = new CPU(this.ram, args.debugCpu); + this.gpu = new GPU( + this.cpu, + this.cart.name, + args.headless, + args.debugGpu, + ); + this.apu = new APU(this.ram, args.silent, args.debugApu); + this.buttons = new Buttons(this.cpu, this.gpu.hw_window); + this.clock = new Clock( + this.buttons, + args.frames, + args.profile, + args.turbo, + ); + } + + async run() { + while (true) { + await this.tick(); + } + } + + async tick() { + this.cpu.tick(); + this.gpu.tick(); + this.buttons.tick(); + await this.clock.tick(); + this.apu.tick(); + } +} diff --git a/js/src/gpu.ts b/js/src/gpu.ts new file mode 100644 index 0000000..13b32ac --- /dev/null +++ b/js/src/gpu.ts @@ -0,0 +1,479 @@ +import { Mem, Interrupt } from "./consts"; +import { CPU } from "./cpu"; +import * as sdl from "./_sdl"; + +const SCALE = 2; + +const rmask = 0x000000ff; +const gmask = 0x0000ff00; +const bmask = 0x00ff0000; +const amask = 0xff000000; + +enum LCDC { + ENABLED = 1 << 7, + WINDOW_MAP = 1 << 6, + WINDOW_ENABLED = 1 << 5, + DATA_SRC = 1 << 4, + BG_MAP = 1 << 3, + OBJ_SIZE = 1 << 2, + OBJ_ENABLED = 1 << 1, + BG_WIN_ENABLED = 1 << 0, +} + +enum Stat { + LYC_INTERRUPT = 1 << 6, + OAM_INTERRUPT = 1 << 5, + VBLANK_INTERRUPT = 1 << 4, + HBLANK_INTERRUPT = 1 << 3, + LYC_EQUAL = 1 << 2, + MODE_BITS = (1 << 1) | (1 << 0), + + HBLANK = 0x00, + VBLANK = 0x01, + OAM = 0x02, + DRAWING = 0x03, +} + +export class GPU { + cpu: CPU; + headless: boolean; + debug: boolean; + hw_window: sdl.SDL_Window | null; + hw_buffer: sdl.SDL_Texture | null; + hw_renderer: sdl.SDL_Renderer | null; + buffer: sdl.SDL_Surface; + renderer: sdl.SDL_Renderer; + colors: Array; + bgp: Array; + obp0: Array; + obp1: Array; + cycle: number; + + constructor(cpu: CPU, title: string, headless: boolean, debug: boolean) { + this.cpu = cpu; + this.headless = headless; + this.debug = debug; + this.cycle = 0; + + // Window + let w = 160, + h = 144; + if (this.debug) { + w = 160 + 256; + h = 144; + } + if (!headless) { + sdl.f.SDL_Init(sdl.f.SDL_INIT_VIDEO); + console.log(sdl.f.SDL_GetError()); + this.hw_window = sdl.f.SDL_CreateWindow( + `RosettaBoy - ${title}`, + sdl.f.SDL_WINDOWPOS_UNDEFINED, + 0x1FFF0000,//sdl.SDL_WINDOWPOS_UNDEFINED, + w * SCALE, + h * SCALE, + sdl.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | + sdl.SDL_WindowFlags.SDL_WINDOW_RESIZABLE + ); + this.hw_renderer = sdl.f.SDL_CreateRenderer(this.hw_window, -1, 0); + sdl.f.SDL_SetHint(sdl.f.SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // vs "linear" + sdl.f.SDL_RenderSetLogicalSize(this.hw_renderer, w, h); + this.hw_buffer = + sdl.f.SDL_CreateTexture(this.hw_renderer, sdl.f.SDL_PIXELFORMAT_ABGR8888, sdl.f.SDL_TEXTUREACCESS_STREAMING, w, h); + } else { + this.hw_window = null; + this.hw_renderer = null; + this.hw_buffer = null; + } + + this.buffer = sdl.f.SDL_CreateRGBSurface(0, w, h, 32, rmask, gmask, bmask, amask); + this.renderer = sdl.f.SDL_CreateSoftwareRenderer(this.buffer); + + // Colors + this.colors = [ + { r: 0x9b, g: 0xbc, b: 0x0f, a: 0xff }, + { r: 0x8b, g: 0xac, b: 0x0f, a: 0xff }, + { r: 0x30, g: 0x62, b: 0x30, a: 0xff }, + { r: 0x0f, g: 0x38, b: 0x0f, a: 0xff }, + ]; + this.bgp = this.colors; + this.obp0 = this.colors; + this.obp1 = this.colors; + // printf("SDL_Init failed: %s\n", SDL_GetError()); + } + + // GPU::~GPU() { + // SDL_FreeSurface(this.buffer); + // if(this.hw_window) SDL_DestroyWindow(this.hw_window); + // SDL_Quit(); + // } + + tick() { + this.cycle++; + + // CPU STOP stops all LCD activity until a button is pressed + if (this.cpu.stop) { + return; + } + + // Check if LCD enabled at all + const lcdc: u8 = this.cpu.ram.get(Mem.LCDC); + if (!(lcdc & LCDC.ENABLED)) { + // When LCD is re-enabled, LY is 0 + // Does it become 0 as soon as disabled?? + this.cpu.ram.set(Mem.LY, 0); + if (!this.debug) { + return; + } + } + + const lx: u8 = this.cycle % 114; + const ly: u8 = Math.floor(this.cycle / 114) % 154; + this.cpu.ram.set(Mem.LY, ly); + + let stat: u8 = this.cpu.ram.get(Mem.STAT); + stat &= ~Stat.MODE_BITS; + stat &= ~Stat.LYC_EQUAL; + + // LYC compare & interrupt + if (ly == this.cpu.ram.get(Mem.LYC)) { + stat |= Stat.LYC_EQUAL; + if (stat & Stat.LYC_INTERRUPT) { + this.cpu.interrupt(Interrupt.STAT); + } + } + + // Set mode + if (lx == 0 && ly < 144) { + stat |= Stat.OAM; + if (stat & Stat.OAM_INTERRUPT) { + this.cpu.interrupt(Interrupt.STAT); + } + } else if (lx == 20 && ly < 144) { + stat |= Stat.DRAWING; + if (ly == 0) { + // TODO: how often should we update palettes? + // Should every pixel reference them directly? + this.update_palettes(); + const c = this.bgp[0]; + //SDL_SetRenderDrawColor(this.renderer, c.r, c.g, c.b, c.a); + //SDL_RenderClear(this.renderer); + } + this.draw_line(ly); + if (ly == 143) { + if (this.debug) { + this.draw_debug(); + } + //if(this.hw_renderer) { + //SDL_UpdateTexture(this.hw_buffer, NULL, this.buffer.pixels, this.buffer.pitch); + //SDL_RenderCopy(this.hw_renderer, this.hw_buffer, NULL, NULL); + //SDL_RenderPresent(this.hw_renderer); + //} + } + } else if (lx == 63 && ly < 144) { + stat |= Stat.HBLANK; + if (stat & Stat.HBLANK_INTERRUPT) { + this.cpu.interrupt(Interrupt.STAT); + } + } else if (lx == 0 && ly == 144) { + stat |= Stat.VBLANK; + if (stat & Stat.VBLANK_INTERRUPT) { + this.cpu.interrupt(Interrupt.STAT); + } + this.cpu.interrupt(Interrupt.VBLANK); + } + this.cpu.ram.set(Mem.STAT, stat); + } + + update_palettes() { + const raw_bgp = this.cpu.ram.get(Mem.BGP); + this.bgp[0] = this.colors[(raw_bgp >> 0) & 0x3]; + this.bgp[1] = this.colors[(raw_bgp >> 2) & 0x3]; + this.bgp[2] = this.colors[(raw_bgp >> 4) & 0x3]; + this.bgp[3] = this.colors[(raw_bgp >> 6) & 0x3]; + + const raw_obp0 = this.cpu.ram.get(Mem.OBP0); + this.obp0[0] = this.colors[(raw_obp0 >> 0) & 0x3]; + this.obp0[1] = this.colors[(raw_obp0 >> 2) & 0x3]; + this.obp0[2] = this.colors[(raw_obp0 >> 4) & 0x3]; + this.obp0[3] = this.colors[(raw_obp0 >> 6) & 0x3]; + + const raw_obp1 = this.cpu.ram.get(Mem.OBP1); + this.obp1[0] = this.colors[(raw_obp1 >> 0) & 0x3]; + this.obp1[1] = this.colors[(raw_obp1 >> 2) & 0x3]; + this.obp1[2] = this.colors[(raw_obp1 >> 4) & 0x3]; + this.obp1[3] = this.colors[(raw_obp1 >> 6) & 0x3]; + } + + draw_debug() { + const lcdc: u8 = this.cpu.ram.get(Mem.LCDC); + + // Tile data + const tile_display_width: u8 = 32; + for (let tile_id = 0; tile_id < 384; tile_id++) { + const xy: sdl.SDL_Point = { + x: 160 + (tile_id % tile_display_width) * 8, + y: Math.floor(tile_id / tile_display_width) * 8, + }; + this.paint_tile(tile_id, xy, this.bgp, false, false); + } + + // Background scroll border + if (lcdc & LCDC.BG_WIN_ENABLED) { + const rect: sdl.SDL_Rect = { x: 0, y: 0, w: 160, h: 144 }; + //SDL_SetRenderDrawColor(this.renderer, 255, 0, 0, 0xFF); + //SDL_RenderDrawRect(this.renderer, &rect); + } + + // Window tiles + if (lcdc & LCDC.WINDOW_ENABLED) { + const wnd_y: u8 = this.cpu.ram.get(Mem.WY); + const wnd_x: u8 = this.cpu.ram.get(Mem.WX); + const rect: sdl.SDL_Rect = { + x: wnd_x - 7, + y: wnd_y, + w: 160, + h: 144, + }; + //SDL_SetRenderDrawColor(this.renderer, 0, 0, 255, 0xFF); + //SDL_RenderDrawRect(this.renderer, &rect); + } + } + + draw_line(ly: number) { + const lcdc: u8 = this.cpu.ram.get(Mem.LCDC); + + // Background tiles + if (lcdc & LCDC.BG_WIN_ENABLED) { + const scroll_y = this.cpu.ram.get(Mem.SCY); + const scroll_x = this.cpu.ram.get(Mem.SCX); + const tile_offset = !(lcdc & LCDC.DATA_SRC); + const tile_map = lcdc & LCDC.BG_MAP ? Mem.Map1 : Mem.Map0; + + if (this.debug) { + const xy: sdl.SDL_Point = { x: 256 - scroll_x, y: ly }; + //SDL_SetRenderDrawColor(this.renderer, 255, 0, 0, 0xFF); + //SDL_RenderDrawPoint(this.renderer, xy.x, xy.y); + } + + const y_in_bgmap = (ly + scroll_y) % 256; + const tile_y = Math.floor(y_in_bgmap / 8); + const tile_sub_y = y_in_bgmap % 8; + + for (let lx = 0; lx <= 160; lx += 8) { + const x_in_bgmap = (lx + scroll_x) % 256; + const tile_x = Math.floor(x_in_bgmap / 8); + const tile_sub_x = x_in_bgmap % 8; + + let tile_id = this.cpu.ram.get(tile_map + tile_y * 32 + tile_x); + if (tile_offset && tile_id < 0x80) { + tile_id += 0x100; + } + const xy: sdl.SDL_Point = { + x: lx - tile_sub_x, + y: ly - tile_sub_y, + }; + this.paint_tile_line( + tile_id, + xy, + this.bgp, + false, + false, + tile_sub_y, + ); + } + } + + // Window tiles + if (lcdc & LCDC.WINDOW_ENABLED) { + const wnd_y = this.cpu.ram.get(Mem.WY); + const wnd_x = this.cpu.ram.get(Mem.WX); + const tile_offset = !(lcdc & LCDC.DATA_SRC); + const tile_map = lcdc & LCDC.WINDOW_MAP ? Mem.Map1 : Mem.Map0; + + // blank out the background + const rect: sdl.SDL_Rect = { + x: wnd_x - 7, + y: wnd_y, + w: 160, + h: 144, + }; + const c = this.bgp[0]; + //SDL_SetRenderDrawColor(this.renderer, c.r, c.g, c.b, c.a); + //SDL_RenderFillRect(this.renderer, &rect); + + const y_in_bgmap = ly - wnd_y; + const tile_y = Math.floor(y_in_bgmap / 8); + const tile_sub_y = y_in_bgmap % 8; + + for (let tile_x = 0; tile_x < 20; tile_x++) { + let tile_id = this.cpu.ram.get(tile_map + tile_y * 32 + tile_x); + if (tile_offset && tile_id < 0x80) { + tile_id += 0x100; + } + const xy: sdl.SDL_Point = { + x: tile_x * 8 + wnd_x - 7, + y: tile_y * 8 + wnd_y, + }; + this.paint_tile_line( + tile_id, + xy, + this.bgp, + false, + false, + tile_sub_y, + ); + } + } + + // Sprites + if (lcdc & LCDC.OBJ_ENABLED) { + const dbl = lcdc & LCDC.OBJ_SIZE; + + // TODO: sorted by x + // const sprites: [Sprite; 40] = []; + // memcpy(sprites, &ram.data[OAM_BASE], 40 * sizeof(Sprite)); + // for sprite in sprites.iter() { + for (let n = 0; n < 40; n++) { + const sprite = new Sprite( + this.cpu.ram.get(Mem.OamBase + 4 * n + 0), + this.cpu.ram.get(Mem.OamBase + 4 * n + 1), + this.cpu.ram.get(Mem.OamBase + 4 * n + 2), + this.cpu.ram.get(Mem.OamBase + 4 * n + 3), + ); + + if (sprite.is_live()) { + const palette = sprite.palette ? this.obp1 : this.obp0; + // printf("Drawing sprite %d (from %04X) at %d,%d\n", tile_id, OAM_BASE + (sprite_id * 4) + 0, x, y); + const xy: sdl.SDL_Point = { + x: sprite.x - 8, + y: sprite.y - 16, + }; + this.paint_tile( + sprite.tile_id, + xy, + palette, + sprite.x_flip, + sprite.y_flip, + ); + + if (dbl) { + xy.y = sprite.y - 8; + this.paint_tile( + sprite.tile_id + 1, + xy, + palette, + sprite.x_flip, + sprite.y_flip, + ); + } + } + } + } + } + + paint_tile( + tile_id: number, + offset: sdl.SDL_Point, + palette: Array, + flip_x: boolean, + flip_y: boolean, + ) { + for (let y = 0; y < 8; y++) { + this.paint_tile_line(tile_id, offset, palette, flip_x, flip_y, y); + } + + if (this.debug) { + const rect: sdl.SDL_Rect = { + x: offset.x, + y: offset.y, + w: 8, + h: 8, + }; + const c = gen_hue(tile_id); + //SDL_SetRenderDrawColor(this.renderer, c.r, c.g, c.b, c.a); + //SDL_RenderDrawRect(this.renderer, &rect); + } + } + + paint_tile_line( + tile_id: number, + offset: sdl.SDL_Point, + palette: Array, + flip_x: boolean, + flip_y: boolean, + y: number, + ) { + const addr: u16 = Mem.TileData + tile_id * 16 + y * 2; + const low_byte: u8 = this.cpu.ram.get(addr); + const high_byte: u8 = this.cpu.ram.get(addr + 1); + for (let x = 0; x < 8; x++) { + const low_bit = (low_byte >> (7 - x)) & 0x01; + const high_bit = (high_byte >> (7 - x)) & 0x01; + const px = (high_bit << 1) | low_bit; + // pallette #0 = transparent, so don't draw anything + if (px > 0) { + const xy: sdl.SDL_Point = { + x: offset.x + (flip_x ? 7 - x : x), + y: offset.y + (flip_y ? 7 - y : y), + }; + const c = palette[px]; + //SDL_SetRenderDrawColor(this.renderer, c.r, c.g, c.b, c.a); + //SDL_RenderDrawPoint(this.renderer, xy.x, xy.y); + } + } + } +} + +class Sprite { + y: u8; + x: u8; + tile_id: u8; + flags: u8; + + constructor(y: u8, x: u8, tile_id: u8, flags: u8) { + this.y = y; + this.x = x; + this.tile_id = tile_id; + this.flags = flags; + } + + is_live(): boolean { + return this.x > 0 && this.x < 168 && this.y > 0 && this.y < 160; + } + + get palette(): boolean { + return (this.flags & (1 << 4)) != 0; + } + get x_flip(): boolean { + return (this.flags & (1 << 5)) != 0; + } + get y_flip(): boolean { + return (this.flags & (1 << 6)) != 0; + } + get behind(): boolean { + return (this.flags & (1 << 7)) != 0; + } +} + +function gen_hue(n: u8): sdl.SDL_Color { + const region: u8 = Math.floor(n / 43); + const remainder: u8 = (n - region * 43) * 6; + + const q: u8 = 255 - remainder; + const t: u8 = remainder; + + switch (region) { + case 0: + return { r: 255, g: t, b: 0, a: 0xff }; + case 1: + return { r: q, g: 255, b: 0, a: 0xff }; + case 2: + return { r: 0, g: 255, b: t, a: 0xff }; + case 3: + return { r: 0, g: q, b: 255, a: 0xff }; + case 4: + return { r: t, g: 0, b: 255, a: 0xff }; + default: + return { r: 255, g: 0, b: q, a: 0xff }; + } +} diff --git a/js/src/index.d.ts b/js/src/index.d.ts new file mode 100644 index 0000000..6b6881f --- /dev/null +++ b/js/src/index.d.ts @@ -0,0 +1,3 @@ +type u8 = number; +type u16 = number; +type i8 = number; diff --git a/js/src/index.ts b/js/src/index.ts new file mode 100644 index 0000000..e7a51b1 --- /dev/null +++ b/js/src/index.ts @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +/// + +import { parse_args } from "./args"; +import { GameBoy } from "./gameboy"; +import { + GameException, + UserException, + ControlledExit, + UnitTestFailed, +} from "./errors"; + +async function main(): Promise { + let args = parse_args(process.argv); + + try { + let gameboy = new GameBoy(args); + await gameboy.run(); + } catch (e) { + if (e instanceof ControlledExit) { + console.log(e.message); + if (e instanceof UnitTestFailed) { + return 2; + } + return 0; + } else if (e instanceof GameException) { + console.log(e); + return 3; + } else if (e instanceof UserException) { + console.log(e); + return 4; + } else { + console.log("Surprise error"); + console.log(e); + return 1; + } + } + return 0; +} + +main().then((code) => process.exit(code)); diff --git a/js/src/ram.ts b/js/src/ram.ts new file mode 100644 index 0000000..3b76735 --- /dev/null +++ b/js/src/ram.ts @@ -0,0 +1,265 @@ +import { Cart } from "./cart"; +import { Mem } from "./consts"; +import { InvalidRamRead, InvalidRamWrite } from "./errors"; +import { hex } from "./_utils"; + +/** + * A minimal boot ROM which just sets values the same as + * the canonical ROM and then gets out of the way - no + * logo scrolling or DRM. + */ +// prettier-ignore +const BOOT: Uint8Array = new Uint8Array([ + // prod memory + 0x31, 0xFE, 0xFF, // LD SP,$FFFE + // enable LCD + 0x3E, 0x91, // LD A,$91 + 0xE0, 0x40, // LDH [Mem::LCDC], A + // set flags + 0x3E, 0x01, // LD A,$01 + 0xCB, 0x7F, // BIT 7,A (sets Z,n,H) + 0x37, // SCF (sets C) + // set registers + 0x3E, 0x01, // LD A,$01 + 0x06, 0x00, // LD B,$00 + 0x0E, 0x13, // LD C,$13 + 0x16, 0x00, // LD D,$00 + 0x1E, 0xD8, // LD E,$D8 + 0x26, 0x01, // LD H,$01 + 0x2E, 0x4D, // LD L,$4D + // skip to the end of the bootloader + 0xC3, 0xFD, 0x00, // JP $00FD + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + // this instruction must be at the end of ROM -- + // after these finish executing, PC needs to be 0x100 + 0xE0, 0x50, // LDH 50,A (disable boot rom) +]); +const ROM_BANK_SIZE: number = 0x4000; +const RAM_BANK_SIZE: number = 0x2000; + +export class RAM { + cart: Cart; + debug: boolean; + + ram_enable: boolean; + ram_bank_mode: boolean; + rom_bank_low: number; + rom_bank_high: number; + rom_bank: number; + ram_bank: number; + boot: Uint8Array; + data: Uint8Array; + + constructor(cart: Cart, debug: boolean) { + this.cart = cart; + this.debug = debug; + + this.ram_enable = false; + this.ram_bank_mode = false; + this.rom_bank_low = 1; + this.rom_bank_high = 0; + this.rom_bank = 1; + this.ram_bank = 0; + this.boot = BOOT; + // FIXME: try loading boot.gb + this.data = new Uint8Array(0x10000); + } + + get(addr: number): number { + let val = this.data[addr]; + if (addr < 0x4000) { + // ROM bank 0 + if (this.data[Mem.BOOT] == 0 && addr < 0x100) val = this.boot[addr]; + else val = this.cart.data[addr]; + } else if (addr < 0x8000) { + // Switchable ROM bank + // TODO: array bounds check + const offset = addr - 0x4000; + const bank = this.rom_bank * ROM_BANK_SIZE; + //console.log(this.rom_bank, bank, offset, this.cart.data[bank + offset]); + val = this.cart.data[bank + offset]; + } else if (addr < 0xa000) { + // VRAM + } else if (addr < 0xc000) { + // 8KB Switchable RAM bank + if (!this.ram_enable) + throw new InvalidRamRead( + `Reading from external ram while disabled: ${hex(addr, 4)}`, + ); + const bank = this.ram_bank * RAM_BANK_SIZE; + const offset = addr - 0xa000; + if (bank + offset >= this.cart.ram_size) { + // this should never happen because we die on ram_bank being + // set to a too-large value + throw new InvalidRamRead( + `Reading from external ram beyond limit: ${hex( + bank + offset, + 4, + )} (${hex(this.ram_bank, 2)}:${hex(offset, 4)})`, + ); + } + val = this.cart.ram[bank + offset]; + } else if (addr < 0xd000) { + // work RAM, bank 0 + } else if (addr < 0xe000) { + // work RAM, bankable in CGB + } else if (addr < 0xfe00) { + // ram[E000-FE00] mirrors ram[C000-DE00] + val = this.data[addr - 0x2000]; + } else if (addr < 0xfea0) { + // Sprite attribute table + } else if (addr < 0xff00) { + // Unusable + val = 0xff; + } else if (addr < 0xff80) { + // IO Registers + } else if (addr < 0xffff) { + // High RAM + } else { + // IE Register + } + + if (this.debug) { + console.log(`ram[${hex(addr, 4)}] -> ${hex(val, 2)}`); + } + return val; + } + + set(addr: number, val: number) { + if (this.debug) { + console.log(`ram[${hex(addr, 4)}] <- ${hex(val, 2)}`); + } + if (val < 0 || val > 0xff || val != Math.floor(val)) { + throw new Error("Invalid ram write"); + } + if (addr < 0x2000) { + this.ram_enable = val != 0; + } else if (addr < 0x4000) { + this.rom_bank_low = val; + this.rom_bank = (this.rom_bank_high << 5) | this.rom_bank_low; + if (this.debug) { + console.log( + "rom_bank set to {}/{}", + this.rom_bank, + this.cart.rom_size / ROM_BANK_SIZE, + ); + } + if (this.rom_bank * ROM_BANK_SIZE > this.cart.rom_size) { + throw new InvalidRamWrite( + "Set rom_bank beyond the size of ROM", + ); + } + } else if (addr < 0x6000) { + if (this.ram_bank_mode) { + this.ram_bank = val; + if (this.debug) { + console.log( + "ram_bank set to {}/{}", + this.ram_bank, + this.cart.ram_size / RAM_BANK_SIZE, + ); + } + if (this.ram_bank * RAM_BANK_SIZE > this.cart.ram_size) { + throw new InvalidRamWrite( + "Set ram_bank beyond the size of RAM", + ); + } + } else { + this.rom_bank_high = val; + this.rom_bank = (this.rom_bank_high << 5) | this.rom_bank_low; + if (this.debug) { + console.log( + "rom_bank set to {}/{}", + this.rom_bank, + this.cart.rom_size / ROM_BANK_SIZE, + ); + } + if (this.rom_bank * ROM_BANK_SIZE > this.cart.rom_size) { + throw new InvalidRamWrite( + "Set rom_bank beyond the size of ROM", + ); + } + } + } else if (addr < 0x8000) { + this.ram_bank_mode = val != 0; + if (this.debug) { + console.log("ram_bank_mode set to {}", this.ram_bank_mode); + } + } else if (addr < 0xa000) { + // VRAM + // TODO: if(writing to tile RAM, update tiles in Mem.class? + } else if (addr < 0xc000) { + // external RAM, bankable + if (!this.ram_enable) { + throw new InvalidRamWrite( + `Writing to external ram while disabled: ${hex( + addr, + 4, + )}=${hex(val, 2)}`, + ); + } + const bank = this.ram_bank * RAM_BANK_SIZE; + const offset = addr - 0xa000; + if (this.debug) { + console.log( + `Writing external RAM: ${hex(bank + offset, 4)}=${hex( + val, + 2, + )} (${hex(this.ram_bank, 2)}:${hex(offset, 4)})`, + ); + } + if (bank + offset >= this.cart.ram_size) { + throw new InvalidRamWrite( + `Writing to external ram beyond limit: ${hex( + bank + offset, + 4, + )} (${hex(this.ram_bank, 2)}:${hex(offset, 4)})`, + ); + } + this.cart.ram[bank + offset] = val; + } else if (addr < 0xd000) { + // work RAM, bank 0 + } else if (addr < 0xe000) { + // work RAM, bankable in CGB + } else if (addr < 0xfe00) { + // ram[E000-FE00] mirrors ram[C000-DE00] + this.data[addr - 0x2000] = val; + } else if (addr < 0xfea0) { + // Sprite attribute table + } else if (addr < 0xff00) { + // Unusable + if (this.debug) { + console.log( + "Writing to invalid ram: {:04x} = {:02x}", + addr, + val, + ); + } + } else if (addr < 0xff80) { + // IO Registers + // if(addr == Mem.:SCX as u16 { + // console.logln!("LY = {}, SCX = {}", this.get(Mem.:LY), val); + // } + } else if (addr < 0xffff) { + // High RAM + } else { + // IE Register + } + + this.data[addr] = val; + } +} diff --git a/js/tsconfig.json b/js/tsconfig.json new file mode 100644 index 0000000..63008a2 --- /dev/null +++ b/js/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "lib": ["es2021"], + "module": "commonjs", + "target": "es2021", + + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node" + } +} \ No newline at end of file