commit 95e15152289c3d6c01fcbd902ba6ab8985a71b44 Author: liyp Date: Mon Mar 6 09:10:48 2023 +0800 first commit diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1503bea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,419 @@ +# Changelog + +## [0.3.5](https://github.com/haishanh/yacd/compare/v0.3.4...v0.3.5) (2022-05-14) + +Added: + +- Added "Auto" theme option for theme to follow system theme preference +- Display rule payload if possible in rule column of connections table +- Allow override default backend url use environment variable with docker container +- Gzip and cache static assets in docker container +- Docker image is now published to ghcr too + +Changed: + +- Use Inter as app wide font + +## [0.3.4](https://github.com/haishanh/yacd/compare/v0.3.3...v0.3.4) (2021-11-14) + +Added: + +- Add float action button to pause/start log streaming + +## [0.3.3](https://github.com/haishanh/yacd/compare/v0.3.2...v0.3.3) (2021-07-19) + +Added: + +- Support switch theme on backend config page +- If / is api server, use it as default + +## [0.3.2](https://github.com/haishanh/yacd/compare/v0.3.1...v0.3.2) (2021-06-07) + +Changed: + +- Change web base to './' + +## [0.3.1](https://github.com/haishanh/yacd/compare/v0.3.0...v0.3.1) (2021-06-06) + +Fixed: + +- Fixed floating action button style + +## [0.3.0](https://github.com/haishanh/yacd/compare/v0.2.15...v0.3.0) (2021-06-05) + +Changed: + +- Switch the build system to use Vite. This should not change much about user experience. +- Style tweaks: + - The light theme now use a light gray background instead of a pure white + - Statistic blocks on Overview are now styled more like a card + - Log type badges are now ellipse shaped + - Config fields are more compact now + +Added: + +- Request logs with configured log level +- Reconnect logs web socket on log level config change + +## [0.2.15](https://github.com/haishanh/yacd/compare/v0.2.14...v0.2.15) (2021-02-28) + +Changed: + +- Display API backend info in title only when there are multiple backends +- Changed the function of floating action button from refresh to update all providers on rules page + +Added: + +- Action button to update all proxies providers on proxies page + +## [0.2.14](https://github.com/haishanh/yacd/compare/v0.2.13...v0.2.14) (2021-01-04) + +Added: + +- support set default Clash API baseURL with data attribute in HTML template (see [details](https://github.com/haishanh/yacd/pull/550)) +- add apple-touch-icon\*.png + +Fixed: + +- encode URI for latency test url + +## [0.2.13](https://github.com/haishanh/yacd/compare/v0.2.12...v0.2.13) (2020-12-06) + +Added: + +- Initial Chinese UI language support + +Fixed: + +- Fix weird scroll behavior on config page + +## [0.2.12](https://github.com/haishanh/yacd/compare/v0.2.11...v0.2.12) (2020-11-24) + +Changed: + +- Some minor accessibility improvements +- Changed log level display order to `debug warning info error silent` + +## [0.2.11](https://github.com/haishanh/yacd/compare/v0.2.10...v0.2.11) (2020-11-09) + +Changed: + +- Display proxy type "Shadowsocks" as "SS" to make proxy item tile more compact + +## [0.2.10](https://github.com/haishanh/yacd/compare/v0.2.9...v0.2.10) (2020-11-06) + +Added: + +- Precache assets with service worker. + +## [0.2.9](https://github.com/haishanh/yacd/compare/v0.2.8...v0.2.9) (2020-11-01) + +Added: + +- Display current backend host in title. + +Changed: + +- Change backend baseURL default port to 9090. + +## [0.2.8](https://github.com/haishanh/yacd/compare/v0.2.7...v0.2.8) (2020-10-12) + +Added: + +- Better error message for filling API base URL without providing a http protocol prefix. + +## [0.2.7](https://github.com/haishanh/yacd/compare/v0.2.6...v0.2.7) (2020-09-13) + +Added: + +- multi backends management (see "Switch backend" action the the bottom of Config page) + +## [0.2.6](https://github.com/haishanh/yacd/compare/v0.2.5...v0.2.6) (2020-09-08) + +Changed: + +- use API base URL instead of hostname and port for Clash backend config + +## [0.2.5](https://github.com/haishanh/yacd/compare/v0.2.4...v0.2.5) (2020-08-30) + +Added: + +- docker image arm and arm64 support + +## [0.2.4](https://github.com/haishanh/yacd/compare/v0.2.3...v0.2.4) (2020-08-11) + +Fixed: + +- fix cannot change mixed port + +## [0.2.3](https://github.com/haishanh/yacd/compare/v0.2.2...v0.2.3) (2020-08-06) + +Changed: + +- use desc sort first for columns with numeric value in connections table + +## [0.2.2](https://github.com/haishanh/yacd/compare/v0.2.1...v0.2.2) (2020-08-01) + +Added: + +- a simple about page + +Removed: + +- logo in sidebar + +## [0.2.1](https://github.com/haishanh/yacd/compare/v0.2.0...v0.2.1) (2020-07-13) + +Fixed: + +- uri-encode API secret for it to be used in url safely + +## [0.2.0](https://github.com/haishanh/yacd/compare/v0.1.25...v0.2.0) (2020-07-04) + +Added: + +- support rule provider + +## [0.1.25](https://github.com/haishanh/yacd/compare/v0.1.24...v0.1.25) (2020-07-01) + +Added: + +- support mixed-port + +## [0.1.24](https://github.com/haishanh/yacd/compare/v0.1.23...v0.1.24) (2020-06-22) + +Fixed: + +- fix can not type in Chinese in proxy text filter input + +## [0.1.23](https://github.com/haishanh/yacd/compare/v0.1.22...v0.1.23) (2020-06-20) + +Added: + +- add a simple filter for proxy names + +Fixed: + +- fix color display for unavailable proxy item + +## [0.1.22](https://github.com/haishanh/yacd/compare/v0.1.21...v0.1.22) (2020-06-18) + +Fixed: + +- fix mode switching +- fix broken "Hide unavailable proxies" setting + +Changed: + +- make proxy group lowest latency item when sorting by latency + +## [0.1.21](https://github.com/haishanh/yacd/compare/v0.1.20...v0.1.21) (2020-06-17) + +Fixed: + +- default to big latency for items with unavailable statistics when sorting + +Added: + +- a toggle to close old connections automatically when switching proxy +- use special color for non-proxy summary view dot item + +## [0.1.20](https://github.com/haishanh/yacd/compare/v0.1.19...v0.1.20) (2020-06-08) + +Changed: + +- switch to Open Sans and reduce emitted font files + +## [0.1.19](https://github.com/haishanh/yacd/compare/v0.1.18...v0.1.19) (2020-06-07) + +Added: + +- modal prompt to close previous connections when switch proxy + +Fixed: + +- mode not display correctly due to clash API change + +Changed: + +- switch primary font family from "Merriweather Sans" to "Inter", also starting to self hosting font files + +## [0.1.18](https://github.com/haishanh/yacd/compare/v0.1.17...v0.1.18) (2020-06-04) + +Added: + +- test latency button for each proxy group + +## [0.1.17](https://github.com/haishanh/yacd/compare/v0.1.16...v0.1.17) (2020-06-03) + +Changed: + +- reduce connections table visual width + +## [0.1.16](https://github.com/haishanh/yacd/compare/v0.1.15...v0.1.16) (2020-05-31) + +Added: + +- filtering connections + +## [0.1.15](https://github.com/haishanh/yacd/compare/v0.1.14...v0.1.15) (2020-05-25) + +Added: + +- add loading status to test latency button + +## [0.1.14](https://github.com/haishanh/yacd/compare/v0.1.13...v0.1.14) (2020-05-17) + +Added: + +- button to pause connection refresh + +Fixed: + +- sorting option accessibility issue due to incorrect background in dark mode + +## [0.1.13](https://github.com/haishanh/yacd/compare/v0.1.12...v0.1.13) (2020-05-01) + +Changed: + +- use color icons in sidebar (experimental) + +## [0.1.12](https://github.com/haishanh/yacd/compare/v0.1.11...v0.1.12) (2020-04-26) + +Features: + +- allow change proxies sorting in group + +## [0.1.11](https://github.com/haishanh/yacd/compare/v0.1.10...v0.1.11) (2020-03-21) + +Features: + +- remembers group collapse state + +## [0.1.10](https://github.com/haishanh/yacd/compare/v0.1.9...v0.1.10) (2020-03-14) + +Fixes: + +- fix broken allow-lan switch + +Features: + +- support set theme with querystring `?theme=dark` or `?theme=light` + +## [0.1.9](https://github.com/haishanh/yacd/compare/v0.1.8...v0.1.9) (2020-03-01) + +Fixes: + +- allow request latency for non-original clash proxy types + +## [0.1.8](https://github.com/haishanh/yacd/compare/v0.1.7...v0.1.8) (2020-03-01) + +Features: + +- support overwrite API hostname in querystring with `?hostname=` +- show current download/upload speed of connections + +## [0.1.7](https://github.com/haishanh/yacd/compare/v0.1.6...v0.1.7) (2020-02-11) + +Refactor: + +- proxies page UI improvement + +Fixes: + +- use destination ip as host if host is an empty string + +## [0.1.6](https://github.com/haishanh/yacd/compare/v0.1.5...v0.1.6) (2020-01-07) + +Features: + +- keep up to 100 closed connections in another tab + +## [0.1.5](https://github.com/haishanh/yacd/compare/v0.1.4...v0.1.5) (2020-01-04) + +Features: + +- support change latency test url #286 + +## [0.1.4](https://github.com/haishanh/yacd/compare/v0.1.3...v0.1.4) (2020-01-03) + +Features: + +- refresh providers and proxies on window regain focus + +Fixes: + +- optimize test latency action when there are providers +- do not show provider section when is no provider + +## [0.1.3](https://github.com/haishanh/yacd/compare/v0.1.2...v0.1.3) (2019-12-27) + +Features: + +- can healthcheck a provider + +## [0.1.2](https://github.com/haishanh/yacd/compare/v0.1.1...v0.1.2) (2019-12-22) + +Fixes: + +- typo in connections table header + +## [0.1.1](https://github.com/haishanh/yacd/compare/v0.1.0...v0.1.1) (2019-12-21) + +Fixes: + +- connections table header data miss alignment + +## [0.1.0](https://github.com/haishanh/yacd/compare/v0.0.10...v0.1.0) (2019-12-20) + +Features: + +- support proxy provider + +## [0.0.10](https://github.com/haishanh/yacd/compare/v0.0.9...v0.0.10) (2019-12-04) + +Features: + +- add upload/download total and connectors number on overview + +## [0.0.9](https://github.com/haishanh/yacd/compare/v0.0.8...v0.0.9) (2019-12-02) + +Fix: + +- specify fab group z-index + +## [0.0.8](https://github.com/haishanh/yacd/compare/v0.0.7...v0.0.8) (2019-12-01) + +Features: + +- support close all connections + +## [0.0.7](https://github.com/haishanh/yacd/compare/v0.0.6...v0.0.7) (2019-11-20) + +Features: + +- use history latency data + +## [0.0.6](https://github.com/haishanh/yacd/compare/v0.0.5...v0.0.6) (2019-11-17) + +Improvements: + +- improve UI for small screens +- connections: update connections table sorting indicator icon +- connections: add place holder when there is no connections data + +## [0.0.5](https://github.com/haishanh/yacd/compare/v0.0.4...v0.0.5) (2019-11-09) + +Features: + +- connections inspection + +## [0.0.4](https://github.com/haishanh/yacd/compare/v0.0.3...v0.0.4) (2019-10-14) + +Features: + +- probing the API server with the given url and auto fill hostname and port + +Internal: + +- upgrade dependencies diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6c6cb4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM --platform=$BUILDPLATFORM node:alpine AS builder +WORKDIR /app + +RUN npm i -g pnpm +COPY pnpm-lock.yaml package.json . +RUN pnpm i + +COPY . . +RUN pnpm build \ + # remove source maps - people like small image + && rm public/*.map || true + +FROM --platform=$TARGETPLATFORM nginx:alpine +COPY docker/nginx-default.conf /etc/nginx/conf.d/default.conf +RUN rm -rf /usr/share/nginx/html/* +COPY --from=builder /app/public /usr/share/nginx/html +ENV YACD_DEFAULT_BACKEND "http://127.0.0.1:9090" +ADD docker-entrypoint.sh / +CMD ["/docker-entrypoint.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2159f5 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +

+ yacd +

+ +> Yet Another [Clash](https://github.com/yaling888/clash) [Dashboard](https://github.com/yaling888/clash-dashboard) + +## Usage + +Install [twemoji](https://github.com/mozilla/twemoji-colr/releases) to display emoji better on Windows system. + + +The site http://yacd.metacubex.one is served with HTTP not HTTPS is because many browsers block requests to HTTP resources from a HTTPS website. If you think it's not safe, you could just download the [zip of the gh-pages](https://github.com/yaling888/yacd/archive/gh-pages.zip), unzip and serve those static files with a web server(like Nginx). + +**Supported URL query params** + +| Param | Description | +| -------- | ---------------------------------------------------------------------------------- | +| hostname | Hostname of the clash backend API (usually the host part of `external-controller`) | +| port | Port of the clash backend API (usually the port part of `external-controller`) | +| secret | Clash API secret (`secret` in your config.yaml) | +| theme | UI color scheme (dark, light, auto) | + +## Development + +```sh +# install dependencies +# you may install pnpm with `npm i -g pnpm` +pnpm i + +# start the dev server +# then go to http://127.0.0.1:3000 +pnpm start + + +# build optimized assets +# ready to deploy assets will be in the directory `public` +pnpm build +``` diff --git a/assets/CNAME b/assets/CNAME new file mode 100644 index 0000000..8f3b90f --- /dev/null +++ b/assets/CNAME @@ -0,0 +1 @@ +yacd.metacubex.one diff --git a/assets/_headers b/assets/_headers new file mode 100644 index 0000000..877d928 --- /dev/null +++ b/assets/_headers @@ -0,0 +1,12 @@ +# for netlify hosting +# https://docs.netlify.com/routing/headers/#syntax-for-the-headers-file + +/* + X-Frame-Options: DENY + X-XSS-Protection: 1; mode=block + X-Content-Type-Options: nosniff + Referrer-Policy: same-origin +/*.css + Cache-Control: public, max-age=31536000, immutable +/*.js + Cache-Control: public, max-age=31536000, immutable diff --git a/assets/apple-touch-icon-precomposed.png b/assets/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..cbb3fcb Binary files /dev/null and b/assets/apple-touch-icon-precomposed.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..1f6323d Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/yacd.ico b/assets/yacd.ico new file mode 100644 index 0000000..0cc77ed Binary files /dev/null and b/assets/yacd.ico differ diff --git a/assets/yacd.png b/assets/yacd.png new file mode 100644 index 0000000..cbb3fcb Binary files /dev/null and b/assets/yacd.png differ diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..68ae7b1 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/sh +sed -i "s|http://127.0.0.1:9090|$YACD_DEFAULT_BACKEND|" /usr/share/nginx/html/index.html +nginx -g "daemon off;" diff --git a/docker/nginx-default.conf b/docker/nginx-default.conf new file mode 100644 index 0000000..3896e25 --- /dev/null +++ b/docker/nginx-default.conf @@ -0,0 +1,31 @@ +server { + listen 80; + server_name localhost; + # access_log /var/log/nginx/host.access.log main; + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + location ~ assets\/.*\.(?:css|js|woff2?|svg|gif|map)$ { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + add_header Cache-Control "max-age=31536000"; + # access_log off; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..8784bea --- /dev/null +++ b/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + yacd + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..a82d42d --- /dev/null +++ b/package.json @@ -0,0 +1,100 @@ +{ + "name": "yacd", + "version": "0.3.5", + "description": "Yet another Clash dashboard", + "author": "Haishan (https://haishan.me)", + "license": "MIT", + "private": true, + "keywords": [ + "react", + "clash" + ], + "scripts": { + "dev": "vite", + "start": "vite", + "build": "vite build", + "serve": "vite preview", + "prepare": "husky install" + }, + "dependencies": { + "@reach/tooltip": "0.18.0", + "@reach/visually-hidden": "0.18.0", + "clsx": "^1.2.1", + "history": "5.3.0", + "invariant": "^2.2.4", + "lodash-es": "^4.17.21", + "memoize-one": "6.0.0", + "modern-normalize": "1.1.0", + "prop-types": "15.8.1", + "react-feather": "^2.0.10", + "react-modal": "3.16.1", + "react-tabs": "6.0.0", + "react-tiny-fab": "4.0.4", + "react-window": "^1.8.8", + "regenerator-runtime": "0.13.11", + "tslib": "2.4.1", + "use-asset": "1.0.4" + }, + "devDependencies": { + "@babel/runtime": "7.20.13", + "@fontsource/inter": "4.5.15", + "@fontsource/roboto-mono": "4.5.10", + "@types/invariant": "2.2.35", + "@types/jest": "29.4.0", + "@types/lodash-es": "4.17.6", + "@types/react": "18.0.27", + "@types/react-dom": "18.0.10", + "@types/react-modal": "3.13.1", + "@types/react-window": "1.8.5", + "@typescript-eslint/eslint-plugin": "5.49.0", + "@typescript-eslint/parser": "5.49.0", + "@vitejs/plugin-react": "3.0.1", + "autoprefixer": "10.4.13", + "chart.js": "4.2.0", + "core-js": "3.27.2", + "cssnano": "5.1.14", + "date-fns": "2.29.3", + "eslint": "8.32.0", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-prettier": "8.6.0", + "eslint-config-react-app": "7.0.1", + "eslint-plugin-flowtype": "8.0.3", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-jest": "27.2.1", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-react": "7.32.1", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-simple-import-sort": "9.0.0", + "framer-motion": "8.5.3", + "husky": "^8.0.3", + "i18next": "22.4.9", + "i18next-browser-languagedetector": "7.0.1", + "i18next-http-backend": "2.1.1", + "immer": "9.0.18", + "lint-staged": "^13.1.0", + "postcss": "8.4.21", + "postcss-preset-env": "^8.0.0", + "prettier": "2.8.3", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-i18next": "12.1.4", + "react-icons": "4.7.1", + "react-query": "3.39.3", + "react-router": "6.8.0", + "react-router-dom": "6.8.0", + "react-switch": "7.0.0", + "react-table": "7.8.0", + "recoil": "0.7.6", + "reselect": "4.1.7", + "resize-observer-polyfill": "^1.5.1", + "sass": "1.57.1", + "typescript": "4.9.4", + "vite": "4.0.4", + "vite-plugin-pwa": "0.14.1", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e253ade --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,7350 @@ +lockfileVersion: 5.4 + +specifiers: + '@babel/runtime': 7.20.13 + '@fontsource/inter': 4.5.15 + '@fontsource/roboto-mono': 4.5.10 + '@reach/tooltip': 0.18.0 + '@reach/visually-hidden': 0.18.0 + '@types/invariant': 2.2.35 + '@types/jest': 29.4.0 + '@types/lodash-es': 4.17.6 + '@types/react': 18.0.27 + '@types/react-dom': 18.0.10 + '@types/react-modal': 3.13.1 + '@types/react-window': 1.8.5 + '@typescript-eslint/eslint-plugin': 5.49.0 + '@typescript-eslint/parser': 5.49.0 + '@vitejs/plugin-react': 3.0.1 + autoprefixer: 10.4.13 + chart.js: 4.2.0 + clsx: ^1.2.1 + core-js: 3.27.2 + cssnano: 5.1.14 + date-fns: 2.29.3 + eslint: 8.32.0 + eslint-config-airbnb-base: 15.0.0 + eslint-config-prettier: 8.6.0 + eslint-config-react-app: 7.0.1 + eslint-plugin-flowtype: 8.0.3 + eslint-plugin-import: 2.27.5 + eslint-plugin-jest: 27.2.1 + eslint-plugin-jsx-a11y: 6.7.1 + eslint-plugin-react: 7.32.1 + eslint-plugin-react-hooks: 4.6.0 + eslint-plugin-simple-import-sort: 9.0.0 + framer-motion: 8.5.3 + history: 5.3.0 + husky: ^8.0.3 + i18next: 22.4.9 + i18next-browser-languagedetector: 7.0.1 + i18next-http-backend: 2.1.1 + immer: 9.0.18 + invariant: ^2.2.4 + lint-staged: ^13.1.0 + lodash-es: ^4.17.21 + memoize-one: 6.0.0 + modern-normalize: 1.1.0 + postcss: 8.4.21 + postcss-preset-env: ^8.0.0 + prettier: 2.8.3 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0 + react-feather: ^2.0.10 + react-i18next: 12.1.4 + react-icons: 4.7.1 + react-modal: 3.16.1 + react-query: 3.39.3 + react-router: 6.8.0 + react-router-dom: 6.8.0 + react-switch: 7.0.0 + react-table: 7.8.0 + react-tabs: 6.0.0 + react-tiny-fab: 4.0.4 + react-window: ^1.8.8 + recoil: 0.7.6 + regenerator-runtime: 0.13.11 + reselect: 4.1.7 + resize-observer-polyfill: ^1.5.1 + sass: 1.57.1 + tslib: 2.4.1 + typescript: 4.9.4 + use-asset: 1.0.4 + vite: 4.0.4 + vite-plugin-pwa: 0.14.1 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-precaching: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + +dependencies: + '@reach/tooltip': 0.18.0_biqbaboplfbrettd7655fr4n2y + '@reach/visually-hidden': 0.18.0_biqbaboplfbrettd7655fr4n2y + clsx: 1.2.1 + history: 5.3.0 + invariant: 2.2.4 + lodash-es: 4.17.21 + memoize-one: 6.0.0 + modern-normalize: 1.1.0 + prop-types: 15.8.1 + react-feather: 2.0.10_react@18.2.0 + react-modal: 3.16.1_biqbaboplfbrettd7655fr4n2y + react-tabs: 6.0.0_react@18.2.0 + react-tiny-fab: 4.0.4_react@18.2.0 + react-window: 1.8.8_biqbaboplfbrettd7655fr4n2y + regenerator-runtime: 0.13.11 + tslib: 2.4.1 + use-asset: 1.0.4_react@18.2.0 + +devDependencies: + '@babel/runtime': 7.20.13 + '@fontsource/inter': 4.5.15 + '@fontsource/roboto-mono': 4.5.10 + '@types/invariant': 2.2.35 + '@types/jest': 29.4.0 + '@types/lodash-es': 4.17.6 + '@types/react': 18.0.27 + '@types/react-dom': 18.0.10 + '@types/react-modal': 3.13.1 + '@types/react-window': 1.8.5 + '@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si + '@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje + '@vitejs/plugin-react': 3.0.1_vite@4.0.4 + autoprefixer: 10.4.13_postcss@8.4.21 + chart.js: 4.2.0 + core-js: 3.27.2 + cssnano: 5.1.14_postcss@8.4.21 + date-fns: 2.29.3 + eslint: 8.32.0 + eslint-config-airbnb-base: 15.0.0_ps7hf4l2dvbuxvtusmrfhmzsba + eslint-config-prettier: 8.6.0_eslint@8.32.0 + eslint-config-react-app: 7.0.1_7uibuqfxkfaozanbtbziikiqje + eslint-plugin-flowtype: 8.0.3_eslint@8.32.0 + eslint-plugin-import: 2.27.5_6savw6y3b7jng6f64kgkyoij64 + eslint-plugin-jest: 27.2.1_sa4tfo476gi7rdzbz5wa2vwvhe + eslint-plugin-jsx-a11y: 6.7.1_eslint@8.32.0 + eslint-plugin-react: 7.32.1_eslint@8.32.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.32.0 + eslint-plugin-simple-import-sort: 9.0.0_eslint@8.32.0 + framer-motion: 8.5.3_biqbaboplfbrettd7655fr4n2y + husky: 8.0.3 + i18next: 22.4.9 + i18next-browser-languagedetector: 7.0.1 + i18next-http-backend: 2.1.1 + immer: 9.0.18 + lint-staged: 13.1.0 + postcss: 8.4.21 + postcss-preset-env: 8.0.0_postcss@8.4.21 + prettier: 2.8.3 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-i18next: 12.1.4_iakk3dtjhjpukdoa4oua5khgci + react-icons: 4.7.1_react@18.2.0 + react-query: 3.39.3_biqbaboplfbrettd7655fr4n2y + react-router: 6.8.0_react@18.2.0 + react-router-dom: 6.8.0_biqbaboplfbrettd7655fr4n2y + react-switch: 7.0.0_biqbaboplfbrettd7655fr4n2y + react-table: 7.8.0_react@18.2.0 + recoil: 0.7.6_biqbaboplfbrettd7655fr4n2y + reselect: 4.1.7 + resize-observer-polyfill: 1.5.1 + sass: 1.57.1 + typescript: 4.9.4 + vite: 4.0.4_sass@1.57.1 + vite-plugin-pwa: 0.14.1_vite@4.0.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-precaching: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + +packages: + + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.16 + dev: true + + /@apideck/better-ajv-errors/0.3.6_ajv@8.11.0: + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + dependencies: + ajv: 8.11.0 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + dev: true + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data/7.19.4: + resolution: {integrity: sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/compat-data/7.20.10: + resolution: {integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.19.3: + resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.4 + '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.3 + '@babel/helper-module-transforms': 7.19.0 + '@babel/helpers': 7.19.4 + '@babel/parser': 7.19.4 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.4 + '@babel/types': 7.19.4 + convert-source-map: 1.8.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/core/7.20.12: + resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helpers': 7.20.13 + '@babel/parser': 7.20.13 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + convert-source-map: 1.8.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/eslint-parser/7.19.1_7zv64ewctsjhrlqbm3wep2xela: + resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} + peerDependencies: + '@babel/core': '>=7.11.0' + eslint: ^7.5.0 || ^8.0.0 + dependencies: + '@babel/core': 7.19.3 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.32.0 + eslint-visitor-keys: 2.1.0 + semver: 6.3.0 + dev: true + + /@babel/generator/7.19.4: + resolution: {integrity: sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + + /@babel/generator/7.20.7: + resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-builder-binary-assignment-operator-visitor/7.18.9: + resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-explode-assignable-expression': 7.18.6 + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.3: + resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.19.4 + '@babel/core': 7.19.3 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.20.10 + '@babel/core': 7.20.12 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-create-class-features-plugin/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-member-expression-to-functions': 7.18.9 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-split-export-declaration': 7.18.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-create-regexp-features-plugin/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + regexpu-core: 5.2.1 + dev: true + + /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12: + resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-explode-assignable-expression/7.18.6: + resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-function-name/7.19.0: + resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-member-expression-to-functions/7.18.9: + resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-module-transforms/7.19.0: + resolution: {integrity: sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.19.4 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-module-transforms/7.20.11: + resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.20.2 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-optimise-call-expression/7.18.6: + resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-plugin-utils/7.19.0: + resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-wrap-function': 7.19.0 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-replace-supers/7.19.1: + resolution: {integrity: sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-member-expression-to-functions': 7.18.9 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-simple-access/7.19.4: + resolution: {integrity: sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-simple-access/7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers/7.18.9: + resolution: {integrity: sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/helper-string-parser/7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier/7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option/7.18.6: + resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-wrap-function/7.19.0: + resolution: {integrity: sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-function-name': 7.19.0 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helpers/7.19.4: + resolution: {integrity: sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helpers/7.20.13: + resolution: {integrity: sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.13 + '@babel/types': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser/7.19.4: + resolution: {integrity: sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/parser/7.20.13: + resolution: {integrity: sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.20.7 + dev: true + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-async-generator-functions/7.19.1_@babel+core@7.20.12: + resolution: {integrity: sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-static-block/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-decorators/7.19.3_@babel+core@7.20.12: + resolution: {integrity: sha512-MbgXtNXqo7RTKYIXVchVJGPvaVufQH3pxvQyfbGvNw1DObIhph+PesYXJTcd8J4DdWibvf6Z2eanOyItX8WnJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/plugin-syntax-decorators': 7.19.0_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-logical-assignment-operators/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-object-rest-spread/7.19.4_@babel+core@7.20.12: + resolution: {integrity: sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.20.10 + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-transform-parameters': 7.18.8_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-optional-chaining/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 + dev: true + + /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-property-in-object/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.12: + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.12: + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.12: + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-decorators/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-flow/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-import-assertions/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.12: + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.12: + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.12: + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.12: + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.12: + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-arrow-functions/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-async-to-generator/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-block-scoping/7.19.4_@babel+core@7.20.12: + resolution: {integrity: sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-classes/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-split-export-declaration': 7.18.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-computed-properties/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-destructuring/7.19.4_@babel+core@7.20.12: + resolution: {integrity: sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-flow-strip-types/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-flow': 7.18.6_@babel+core@7.20.12 + dev: true + + /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.12: + resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-modules-amd/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helper-plugin-utils': 7.19.0 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-commonjs/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-simple-access': 7.20.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-systemjs/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-identifier': 7.19.1 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helper-plugin-utils': 7.19.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex/7.19.1_@babel+core@7.20.12: + resolution: {integrity: sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-replace-supers': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-parameters/7.18.8_@babel+core@7.20.12: + resolution: {integrity: sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.12 + dev: true + + /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.12: + resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 + '@babel/types': 7.20.7 + dev: true + + /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-regenerator/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + regenerator-transform: 0.15.0 + dev: true + + /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-runtime/7.19.1_@babel+core@7.20.12: + resolution: {integrity: sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 + babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 + babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-spread/7.19.0_@babel+core@7.20.12: + resolution: {integrity: sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + dev: true + + /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.12: + resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-typescript/7.19.3_@babel+core@7.20.12: + resolution: {integrity: sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.12: + resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/preset-env/7.19.4_@babel+core@7.20.12: + resolution: {integrity: sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.19.4 + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-proposal-async-generator-functions': 7.19.1_@babel+core@7.20.12 + '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-object-rest-spread': 7.19.4_@babel+core@7.20.12 + '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-import-assertions': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-block-scoping': 7.19.4_@babel+core@7.20.12 + '@babel/plugin-transform-classes': 7.19.0_@babel+core@7.20.12 + '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-destructuring': 7.19.4_@babel+core@7.20.12 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.20.12 + '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-modules-amd': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-modules-commonjs': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-modules-systemjs': 7.19.0_@babel+core@7.20.12 + '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.20.12 + '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-parameters': 7.18.8_@babel+core@7.20.12 + '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-spread': 7.19.0_@babel+core@7.20.12 + '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.12 + '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.12 + '@babel/preset-modules': 0.1.5_@babel+core@7.20.12 + '@babel/types': 7.20.7 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 + babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 + babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 + core-js-compat: 3.25.5 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-modules/0.1.5_@babel+core@7.20.12: + resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 + '@babel/types': 7.20.7 + esutils: 2.0.3 + dev: true + + /@babel/preset-react/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.12 + '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.20.12 + dev: true + + /@babel/preset-typescript/7.18.6_@babel+core@7.20.12: + resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-transform-typescript': 7.19.3_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/runtime/7.20.13: + resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + + /@babel/template/7.18.10: + resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.20.13 + '@babel/types': 7.20.7 + dev: true + + /@babel/template/7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.20.13 + '@babel/types': 7.20.7 + dev: true + + /@babel/traverse/7.19.4: + resolution: {integrity: sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.13 + '@babel/types': 7.20.7 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/traverse/7.20.13: + resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.13 + '@babel/types': 7.20.7 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.19.4: + resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + + /@babel/types/7.20.7: + resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + + /@csstools/cascade-layer-name-parser/1.0.0_jhntdqzgrqlkpxki6fy253alji: + resolution: {integrity: sha512-JxdLxJMDximX1vxCFJdwC7MD4aXNSFbOxBZuYKg2FEz4MLR0UFVmamPtzthzqzxAcU0K6ShvEFfMBrEEb16U+A==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.0.0 + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-parser-algorithms': 2.0.0_wcbxa2wg3evugsqpd27xwuyb6a + '@csstools/css-tokenizer': 2.0.0 + dev: true + + /@csstools/css-parser-algorithms/1.0.0_yc2lywpo4a5sk4h3mmwqkmetvu: + resolution: {integrity: sha512-lPphY34yfV15tEXiz/SYaU8hwqAhbAwqiTExv5tOfc7QZxT70VVYrsiPBaX1osdWZFowrDEAhHe4H3JnyzbjhA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^1.0.0 + dependencies: + '@csstools/css-tokenizer': 1.0.0 + dev: true + + /@csstools/css-parser-algorithms/2.0.0_wcbxa2wg3evugsqpd27xwuyb6a: + resolution: {integrity: sha512-RbukP8OjQvuH85veuzOq8abPjsvqvleZaQC6W0GJFGpwLUh8XmFMQjvtuIM9bQ589YFx4lwwAcSwN4nfcvxIEw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-tokenizer': 2.0.0 + dev: true + + /@csstools/css-tokenizer/1.0.0: + resolution: {integrity: sha512-xdFjdQ+zqqkOsmee+kYRieZD9Cqh4hr01YBQ2/8NtTkMMxbtRX18MC50LX6cMrtaLryqmIdZHN9e16/l0QqnQw==} + engines: {node: ^14 || ^16 || >=18} + dev: true + + /@csstools/css-tokenizer/2.0.0: + resolution: {integrity: sha512-IB6EFP0Hc/YEz1sJVD47oFqJP6TXMB+OW1jXSYnOk5g+6wpk2/zkuBa0gm5edIMM9nVUZ3hF0xCBnyFbK5OIyg==} + engines: {node: ^14 || ^16 || >=18} + dev: true + + /@csstools/media-query-list-parser/1.0.0_oycmb73qbeg4xspzoh5ywrqikm: + resolution: {integrity: sha512-HsTj5ejI8NKKZ4IEd6kK2kQZA/JmIVlUV8+XvO/YS9ntrlYPnbmFT3rkqtbxOVfEafblYCNOpeNw1c+fKGkAqw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^1.0.0 + '@csstools/css-tokenizer': ^1.0.0 + dependencies: + '@csstools/css-parser-algorithms': 1.0.0_yc2lywpo4a5sk4h3mmwqkmetvu + '@csstools/css-tokenizer': 1.0.0 + dev: true + + /@csstools/media-query-list-parser/2.0.0_jhntdqzgrqlkpxki6fy253alji: + resolution: {integrity: sha512-84kEbyJjh2T4Lnz8EkVQrwNANP+dtNb0SDkI3P7kqKnGorPknQUuq8Iqf2v5UsaH08XzPp3ouVJNsyPOdI2B/Q==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.0.0 + '@csstools/css-tokenizer': ^2.0.0 + dependencies: + '@csstools/css-parser-algorithms': 2.0.0_wcbxa2wg3evugsqpd27xwuyb6a + '@csstools/css-tokenizer': 2.0.0 + dev: true + + /@csstools/postcss-cascade-layers/3.0.0_postcss@8.4.21: + resolution: {integrity: sha512-CpjItl5zYoROgc3f7+ptPOXJr0sFDf+0CUEmwQMd/JOAmpP9scXtAWvoFwkFKIjBDE6swqbamjIMwmYVCBL6rg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.0_jwkxkvlpbeeukrxssifiwvrjeq + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /@csstools/postcss-color-function/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-rB3Ur+Ns2aOEBMz3zFyjmU6uBBGko84yRD1G6Re/v2p1GRzjz8/o738syQLoxRb1TNcND0YsiKoUeaRuwfk9yw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-font-format-keywords/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-/ew2ScjfoN4dBWhs3CQhVp47VPVO4MVzTdpjygiW9bXj3e2MyoMXuWoBksA1LdREMh5UhrEjBmtwFIx7R2oVIA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-hwb-function/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-1rXMiRapGL6tQJsGKzvEY78DSDc4lZXQzPkHwRBuwoJvRAdtXWQyR00Jk73TOFkmVtmo5bdbS6Rnm1f01lpmlg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-ic-unit/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-R120sQLhdNFH1K3cBpTLAwqu41L9r9BF5K+6tWMMbCpSiUXhNcYCJWbghf2546No9HBiWGL8YLZabsuaHKAEKg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-is-pseudo-class/3.0.0_postcss@8.4.21: + resolution: {integrity: sha512-rz3Ch3gxMeOdMDppuikEmlI3jhnkQd/OM/xAmSjPeCfhYPy37BlNTgsxpaPVizkhkMOfibH/eMkzeXNbXdBBXw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.0_jwkxkvlpbeeukrxssifiwvrjeq + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /@csstools/postcss-logical-float-and-clear/1.0.0_postcss@8.4.21: + resolution: {integrity: sha512-duwyZx5NHEi3DYtk8vTnZ/QwxXYIm7OaXPEfDflbwEIlpB1wNbY9m7TnLT4vZRPv6yAxHEDoHJXS/Zj36mC7uw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /@csstools/postcss-logical-resize/1.0.0_postcss@8.4.21: + resolution: {integrity: sha512-gsySHNBxhHI76I0MmoHz+OJ+EWlEl7TQFKdpRmKG0bfdYZVZaAexAHXLulJL3Tp3ifcD0ev9fSsIPYGeKTx2qA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-logical-viewport-units/1.0.1_postcss@8.4.21: + resolution: {integrity: sha512-KAZyurROvdd0oFsD33LBXQYq15r25MOpPnpSsZvnPnPIFM0I9XL0EDPILgKiRRQbdjx0SvCWRtxtT4BiOkxpig==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/css-tokenizer': 2.0.0 + postcss: 8.4.21 + dev: true + + /@csstools/postcss-media-queries-aspect-ratio-number-values/1.0.0_postcss@8.4.21: + resolution: {integrity: sha512-gC5RQSI/42TbaOdPZoObcL4lhLYggBzTp/PTypcmLMp8JLPQdlJq2Ys0t8pxfDw98GvsHZahUbhPxJaebbCT1w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/css-parser-algorithms': 1.0.0_yc2lywpo4a5sk4h3mmwqkmetvu + '@csstools/css-tokenizer': 1.0.0 + '@csstools/media-query-list-parser': 1.0.0_oycmb73qbeg4xspzoh5ywrqikm + postcss: 8.4.21 + dev: true + + /@csstools/postcss-nested-calc/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-omq/2O2ZENbCa/UY9i6uo4MloNMXZg/lqMv0utOwlNFUgdrpMdnDKWcNRiHV5OvlOnvg188MRyzHwQjqapbJ7w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-normalize-display-values/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-mN5ycsvVkPjt81fVo33p142imrhSBN3LAvvuP6qfkQ6C3452IWO1ZcbYQ/QYcYBymChsmbXmk1jQNZ2kZOFZag==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-oklab-function/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-PONCTLUqo0GFGG+adf6ZUJt7icC8s1SYxC18S9AQhRAGp4m9Fe9EnpaJofikCuZYHkrxr8ngh6D1mSHwA9aryQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-progressive-custom-properties/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-jQl/sQRcMwgaWWzmy65LtxVWnSvRFnhnaRVCvHaMLf5ZT2LRE5w3EG7Z4FqV6IK4L/vB/GSZSzl8AEA9CXN8Zg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-scope-pseudo-class/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-YvsluKH7w/R86BLDl9urwpMUc5PAdeRdrWj20AcHCmr9+2FdsIuWZxYWnof6kNdVYGpK1ox7lXXuCz06VBowPw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /@csstools/postcss-stepped-value-functions/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-6PWwbiolpSx9Lbmdq39wvt9XKYjWlnIq5cRkAKrZA1lJsL09GRmYdwjYjBuESe6mrkysOED4CIMwEJ+EbginLA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-text-decoration-shorthand/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-+Ce+VVbEj2/TJUGUULiMyC/30JruYpCcADwWSW8kLUoO35XWQWQW09MCRFhTS50AHvIuzYG8lZ5TeFXuVAt/uw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-trigonometric-functions/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-T7SZreVZUX0b2m3+X25Bp7cW2eJWfsCm40NoERSZPxsW6He8zuz+Hx6opOVelMyoM8YvYIBOFns8eNCHX7WL/Q==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /@csstools/postcss-unset-value/2.0.0_postcss@8.4.21: + resolution: {integrity: sha512-dx15RWSMlYsgtcl4YkVOe7Rd8tEEx8AEBnxpURI7AQa4dDJC3utZWD3am4Ynx2aoGu5C5lMF/RvSA3+uRpHPXw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /@csstools/selector-specificity/2.1.0_jwkxkvlpbeeukrxssifiwvrjeq: + resolution: {integrity: sha512-zJ6hb3FDgBbO8d2e83vg6zq7tNvDqSq9RwdwfzJ8tdm9JHNvANq2fqwyRn6mlpUb7CwTs5ILdUrGwi9Gk4vY5w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + postcss-selector-parser: ^6.0.10 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /@emotion/is-prop-valid/0.8.8: + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + requiresBuild: true + dependencies: + '@emotion/memoize': 0.7.4 + dev: true + optional: true + + /@emotion/memoize/0.7.4: + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + dev: true + optional: true + + /@esbuild/android-arm/0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64/0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64/0.16.17: + resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64/0.16.17: + resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64/0.16.17: + resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64/0.16.17: + resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64/0.16.17: + resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm/0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64/0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32/0.16.17: + resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.16.17: + resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el/0.16.17: + resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64/0.16.17: + resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64/0.16.17: + resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x/0.16.17: + resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64/0.16.17: + resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64/0.16.17: + resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64/0.16.17: + resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64/0.16.17: + resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64/0.16.17: + resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32/0.16.17: + resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64/0.16.17: + resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc/1.4.1: + resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.4.0 + globals: 13.19.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@fontsource/inter/4.5.15: + resolution: {integrity: sha512-FzleM9AxZQK2nqsTDtBiY0PMEVWvnKnuu2i09+p6DHvrHsuucoV2j0tmw+kAT3L4hvsLdAIDv6MdGehsPIdT+Q==} + dev: true + + /@fontsource/roboto-mono/4.5.10: + resolution: {integrity: sha512-KrJdmkqz6DszT2wV/bbhXef4r0hV3B0vw2mAqei8A2kRnvq+gcJLmmIeQ94vu9VEXrUQzos5M9lH1TAAXpRphw==} + dev: true + + /@humanwhocodes/config-array/0.11.8: + resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer/1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jest/expect-utils/29.1.2: + resolution: {integrity: sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.0.0 + dev: true + + /@jest/schemas/29.0.0: + resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.24.44 + dev: true + + /@jest/types/29.1.2: + resolution: {integrity: sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.8.3 + '@types/yargs': 17.0.13 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.16 + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map/0.3.2: + resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} + dependencies: + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.16 + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.16: + resolution: {integrity: sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@kurkle/color/0.3.2: + resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} + dev: true + + /@motionone/animation/10.15.1: + resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==} + dependencies: + '@motionone/easing': 10.15.1 + '@motionone/types': 10.15.1 + '@motionone/utils': 10.15.1 + tslib: 2.4.1 + dev: true + + /@motionone/dom/10.15.5: + resolution: {integrity: sha512-Xc5avlgyh3xukU9tydh9+8mB8+2zAq+WlLsC3eEIp7Ax7DnXgY7Bj/iv0a4X2R9z9ZFZiaXK3BO0xMYHKbAAdA==} + dependencies: + '@motionone/animation': 10.15.1 + '@motionone/generators': 10.15.1 + '@motionone/types': 10.15.1 + '@motionone/utils': 10.15.1 + hey-listen: 1.0.8 + tslib: 2.4.1 + dev: true + + /@motionone/easing/10.15.1: + resolution: {integrity: sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==} + dependencies: + '@motionone/utils': 10.15.1 + tslib: 2.4.1 + dev: true + + /@motionone/generators/10.15.1: + resolution: {integrity: sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==} + dependencies: + '@motionone/types': 10.15.1 + '@motionone/utils': 10.15.1 + tslib: 2.4.1 + dev: true + + /@motionone/types/10.15.1: + resolution: {integrity: sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==} + dev: true + + /@motionone/utils/10.15.1: + resolution: {integrity: sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==} + dependencies: + '@motionone/types': 10.15.1 + hey-listen: 1.0.8 + tslib: 2.4.1 + dev: true + + /@nicolo-ribaudo/eslint-scope-5-internals/5.1.1-v1: + resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + dependencies: + eslint-scope: 5.1.1 + dev: true + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@reach/auto-id/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-XwY1IwhM7mkHZFghhjiqjQ6dstbOdpbFLdggeke75u8/8icT8uEHLbovFUgzKjy9qPvYwZIB87rLiR8WdtOXCg==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/utils': 0.18.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@reach/observe-rect/1.2.0: + resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} + dev: false + + /@reach/polymorphic/0.18.0_react@18.2.0: + resolution: {integrity: sha512-N9iAjdMbE//6rryZZxAPLRorzDcGBnluf7YQij6XDLiMtfCj1noa7KyLpEc/5XCIB/EwhX3zCluFAwloBKdblA==} + peerDependencies: + react: ^16.8.0 || 17.x + dependencies: + react: 18.2.0 + dev: false + + /@reach/portal/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-TImozRapd576ofRk30Le2L3lRTFXF1p47B182wnp5eMTdZa74JX138BtNGEPJFOyrMaVmguVF8SSwZ6a0fon1Q==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/utils': 0.18.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@reach/rect/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-Xk8urN4NLn3F70da/DtByMow83qO6DF6vOxpLjuDBqud+kjKgxAU9vZMBSZJyH37+F8mZinRnHyXtlLn5njQOg==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/observe-rect': 1.2.0 + '@reach/utils': 0.18.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@reach/tooltip/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-yugoTmTjB3qoMk/nUvcnw99MqpyE2TQMOXE29qnQhSqHriRwQhfftjXlTAGTSzsUJmbyms3A/1gQW0X61kjFZw==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/auto-id': 0.18.0_biqbaboplfbrettd7655fr4n2y + '@reach/polymorphic': 0.18.0_react@18.2.0 + '@reach/portal': 0.18.0_biqbaboplfbrettd7655fr4n2y + '@reach/rect': 0.18.0_biqbaboplfbrettd7655fr4n2y + '@reach/utils': 0.18.0_biqbaboplfbrettd7655fr4n2y + '@reach/visually-hidden': 0.18.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@reach/utils/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-KdVMdpTgDyK8FzdKO9SCpiibuy/kbv3pwgfXshTI6tEcQT1OOwj7BAksnzGC0rPz0UholwC+AgkqEl3EJX3M1A==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@reach/visually-hidden/0.18.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-NsJ3oeHJtPc6UOeV6MHMuzQ5sl1ouKhW85i3C0S7VM+klxVlYScBZ2J4UVnWB50A2c+evdVpCnld2YeuyYYwBw==} + peerDependencies: + react: ^16.8.0 || 17.x || 18.x + react-dom: ^16.8.0 || 17.x || 18.x + dependencies: + '@reach/polymorphic': 0.18.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@remix-run/router/1.3.1: + resolution: {integrity: sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw==} + engines: {node: '>=14'} + dev: true + + /@rollup/plugin-babel/5.3.1_3dsfpkpoyvuuxyfgdbpn4j4uzm: + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.18.6 + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-node-resolve/11.2.1_rollup@2.79.1: + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.2.2 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-replace/2.4.2_rollup@2.79.1: + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + magic-string: 0.25.9 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-replace/5.0.2_rollup@3.11.0: + resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2_rollup@3.11.0 + magic-string: 0.27.0 + rollup: 3.11.0 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@2.79.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.1 + dev: true + + /@rollup/pluginutils/5.0.2_rollup@3.11.0: + resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.0 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 3.11.0 + dev: true + + /@rushstack/eslint-patch/1.2.0: + resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} + dev: true + + /@sinclair/typebox/0.24.44: + resolution: {integrity: sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg==} + dev: true + + /@surma/rollup-plugin-off-main-thread/2.2.3: + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + dependencies: + ejs: 3.1.8 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.8 + dev: true + + /@trysound/sax/0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + + /@types/estree/0.0.39: + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + dev: true + + /@types/estree/1.0.0: + resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + dev: true + + /@types/invariant/2.2.35: + resolution: {integrity: sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==} + dev: true + + /@types/istanbul-lib-coverage/2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + + /@types/istanbul-lib-report/3.0.0: + resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + dev: true + + /@types/istanbul-reports/3.0.1: + resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + dependencies: + '@types/istanbul-lib-report': 3.0.0 + dev: true + + /@types/jest/29.4.0: + resolution: {integrity: sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==} + dependencies: + expect: 29.1.2 + pretty-format: 29.1.2 + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/json5/0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/lodash-es/4.17.6: + resolution: {integrity: sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==} + dependencies: + '@types/lodash': 4.14.186 + dev: true + + /@types/lodash/4.14.186: + resolution: {integrity: sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==} + dev: true + + /@types/node/18.8.3: + resolution: {integrity: sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==} + dev: true + + /@types/parse-json/4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + dev: true + + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: true + + /@types/react-dom/18.0.10: + resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==} + dependencies: + '@types/react': 18.0.27 + dev: true + + /@types/react-modal/3.13.1: + resolution: {integrity: sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==} + dependencies: + '@types/react': 18.0.27 + dev: true + + /@types/react-window/1.8.5: + resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==} + dependencies: + '@types/react': 18.0.27 + dev: true + + /@types/react/18.0.27: + resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + dev: true + + /@types/resolve/1.17.1: + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + dependencies: + '@types/node': 18.8.3 + dev: true + + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: true + + /@types/semver/7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@types/stack-utils/2.0.1: + resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true + + /@types/trusted-types/2.0.2: + resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} + dev: true + + /@types/yargs-parser/21.0.0: + resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + dev: true + + /@types/yargs/17.0.13: + resolution: {integrity: sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==} + dependencies: + '@types/yargs-parser': 21.0.0 + dev: true + + /@typescript-eslint/eslint-plugin/5.49.0_iu322prlnwsygkcra5kbpy22si: + resolution: {integrity: sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje + '@typescript-eslint/scope-manager': 5.49.0 + '@typescript-eslint/type-utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje + '@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje + debug: 4.3.4 + eslint: 8.32.0 + ignore: 5.2.0 + natural-compare-lite: 1.4.0 + regexpp: 3.2.0 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.4 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/experimental-utils/5.39.0_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-n5N9kG/oGu2xXhHzsWzn94s6CWoiUj59FPU2dF2IQZxPftw+q6Jm5sV2vj5qTgAElRooHhrgtl2gxBQDCPt6WA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.39.0_7uibuqfxkfaozanbtbziikiqje + eslint: 8.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/parser/5.49.0_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.49.0 + '@typescript-eslint/types': 5.49.0 + '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 + debug: 4.3.4 + eslint: 8.32.0 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.39.0: + resolution: {integrity: sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/visitor-keys': 5.39.0 + dev: true + + /@typescript-eslint/scope-manager/5.49.0: + resolution: {integrity: sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.49.0 + '@typescript-eslint/visitor-keys': 5.49.0 + dev: true + + /@typescript-eslint/type-utils/5.49.0_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 + '@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje + debug: 4.3.4 + eslint: 8.32.0 + tsutils: 3.21.0_typescript@4.9.4 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.39.0: + resolution: {integrity: sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/types/5.49.0: + resolution: {integrity: sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.39.0_typescript@4.9.4: + resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/visitor-keys': 5.39.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.4 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree/5.49.0_typescript@4.9.4: + resolution: {integrity: sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.49.0 + '@typescript-eslint/visitor-keys': 5.49.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.4 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.39.0_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 5.39.0 + '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/typescript-estree': 5.39.0_typescript@4.9.4 + eslint: 8.32.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils/5.49.0_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.49.0 + '@typescript-eslint/types': 5.49.0 + '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 + eslint: 8.32.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.32.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.39.0: + resolution: {integrity: sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.39.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@typescript-eslint/visitor-keys/5.49.0: + resolution: {integrity: sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.49.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@vitejs/plugin-react/3.0.1_vite@4.0.4: + resolution: {integrity: sha512-mx+QvYwIbbpOIJw+hypjnW1lAbKDHtWK5ibkF/V1/oMBu8HU/chb+SnqJDAsLq1+7rGqjktCEomMTM5KShzUKQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12 + magic-string: 0.27.0 + react-refresh: 0.14.0 + vite: 4.0.4_sass@1.57.1 + transitivePeerDependencies: + - supports-color + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.0: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.0 + dev: true + + /acorn/8.8.0: + resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /aggregate-error/3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv/8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-escapes/4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex/6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles/5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles/6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query/5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + dependencies: + deep-equal: 2.2.0 + dev: true + + /array-includes/3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + get-intrinsic: 1.1.3 + is-string: 1.0.7 + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat/1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap/1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted/1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.1.3 + dev: true + + /ast-types-flow/0.0.7: + resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} + dev: true + + /astral-regex/2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /async/3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: true + + /at-least-node/1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: true + + /autoprefixer/10.4.13_postcss@8.4.21: + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.4 + caniuse-lite: 1.0.30001448 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /axe-core/4.6.3: + resolution: {integrity: sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==} + engines: {node: '>=4'} + dev: true + + /axobject-query/3.1.1: + resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} + dependencies: + deep-equal: 2.2.0 + dev: true + + /babel-plugin-dynamic-import-node/2.3.3: + resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} + dependencies: + object.assign: 4.1.4 + dev: true + + /babel-plugin-macros/3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.20.13 + cosmiconfig: 7.0.1 + resolve: 1.22.1 + dev: true + + /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.12: + resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.20.10 + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.12: + resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + core-js-compat: 3.25.5 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.12: + resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-transform-react-remove-prop-types/0.4.24: + resolution: {integrity: sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==} + dev: true + + /babel-preset-react-app/10.0.1: + resolution: {integrity: sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==} + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-decorators': 7.19.3_@babel+core@7.20.12 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12 + '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-flow-strip-types': 7.19.0_@babel+core@7.20.12 + '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-runtime': 7.19.1_@babel+core@7.20.12 + '@babel/preset-env': 7.19.4_@babel+core@7.20.12 + '@babel/preset-react': 7.18.6_@babel+core@7.20.12 + '@babel/preset-typescript': 7.18.6_@babel+core@7.20.12 + '@babel/runtime': 7.20.13 + babel-plugin-macros: 3.1.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + transitivePeerDependencies: + - supports-color + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /big-integer/1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /boolbase/1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion/2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /broadcast-channel/3.7.0: + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} + dependencies: + '@babel/runtime': 7.20.13 + detect-node: 2.1.0 + js-sha3: 0.8.0 + microseconds: 0.2.0 + nano-time: 1.0.0 + oblivious-set: 1.0.0 + rimraf: 3.0.2 + unload: 2.2.0 + dev: true + + /browserslist/4.21.4: + resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001448 + electron-to-chromium: 1.4.276 + node-releases: 2.0.6 + update-browserslist-db: 1.0.10_browserslist@4.21.4 + dev: true + + /buffer-from/1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /builtin-modules/3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.3 + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /caniuse-api/3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + dependencies: + browserslist: 4.21.4 + caniuse-lite: 1.0.30001448 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + dev: true + + /caniuse-lite/1.0.30001448: + resolution: {integrity: sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA==} + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chart.js/4.2.0: + resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==} + engines: {pnpm: ^7.0.0} + dependencies: + '@kurkle/color': 0.3.2 + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /ci-info/3.5.0: + resolution: {integrity: sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==} + dev: true + + /clean-stack/2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + + /cli-cursor/3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-truncate/2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + + /cli-truncate/3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + dev: true + + /clsx/1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /colord/2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + dev: true + + /colorette/2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + dev: true + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander/7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + + /commander/9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + dev: true + + /common-tags/1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /confusing-browser-globals/1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + dev: true + + /convert-source-map/1.8.0: + resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /core-js-compat/3.25.5: + resolution: {integrity: sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==} + dependencies: + browserslist: 4.21.4 + dev: true + + /core-js/3.27.2: + resolution: {integrity: sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==} + requiresBuild: true + dev: true + + /cosmiconfig/7.0.1: + resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cross-fetch/3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /crypto-random-string/2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: true + + /css-blank-pseudo/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-2QwvERc+e7bWoO6Cva1goJR3r/qe2opbizEWpWEtKAxW9KDpEovI2Y8M2UgqoEVQyPAsWJwWnBpSpItqvjveoQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /css-declaration-sorter/6.3.1_postcss@8.4.21: + resolution: {integrity: sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + dependencies: + postcss: 8.4.21 + dev: true + + /css-has-pseudo/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-vFe2z1/y8xG3JiJCAMOoCCXCwSbG2ndQJqFVVaFHoSuaEmvni8VNuFTC9IAYmqJU7c5elPEXJm40i/x5Zk0GSQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.0_jwkxkvlpbeeukrxssifiwvrjeq + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + postcss-value-parser: 4.2.0 + dev: true + + /css-prefers-color-scheme/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-Fb6GOyJRTI3YZ1v0ySi/X+at+ImRGgySsHvAXYsFo62aTa+ClaMi8E9R/oQmJmD8WPpNHgZXQ1nhkXbCCCne3g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /css-select/4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + dev: true + + /css-tree/1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: true + + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + + /cssdb/7.4.1: + resolution: {integrity: sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==} + dev: true + + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cssnano-preset-default/5.2.13_postcss@8.4.21: + resolution: {integrity: sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + css-declaration-sorter: 6.3.1_postcss@8.4.21 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-calc: 8.2.4_postcss@8.4.21 + postcss-colormin: 5.3.0_postcss@8.4.21 + postcss-convert-values: 5.1.3_postcss@8.4.21 + postcss-discard-comments: 5.1.2_postcss@8.4.21 + postcss-discard-duplicates: 5.1.0_postcss@8.4.21 + postcss-discard-empty: 5.1.1_postcss@8.4.21 + postcss-discard-overridden: 5.1.0_postcss@8.4.21 + postcss-merge-longhand: 5.1.7_postcss@8.4.21 + postcss-merge-rules: 5.1.3_postcss@8.4.21 + postcss-minify-font-values: 5.1.0_postcss@8.4.21 + postcss-minify-gradients: 5.1.1_postcss@8.4.21 + postcss-minify-params: 5.1.4_postcss@8.4.21 + postcss-minify-selectors: 5.2.1_postcss@8.4.21 + postcss-normalize-charset: 5.1.0_postcss@8.4.21 + postcss-normalize-display-values: 5.1.0_postcss@8.4.21 + postcss-normalize-positions: 5.1.1_postcss@8.4.21 + postcss-normalize-repeat-style: 5.1.1_postcss@8.4.21 + postcss-normalize-string: 5.1.0_postcss@8.4.21 + postcss-normalize-timing-functions: 5.1.0_postcss@8.4.21 + postcss-normalize-unicode: 5.1.1_postcss@8.4.21 + postcss-normalize-url: 5.1.0_postcss@8.4.21 + postcss-normalize-whitespace: 5.1.1_postcss@8.4.21 + postcss-ordered-values: 5.1.3_postcss@8.4.21 + postcss-reduce-initial: 5.1.1_postcss@8.4.21 + postcss-reduce-transforms: 5.1.0_postcss@8.4.21 + postcss-svgo: 5.1.0_postcss@8.4.21 + postcss-unique-selectors: 5.1.1_postcss@8.4.21 + dev: true + + /cssnano-utils/3.1.0_postcss@8.4.21: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /cssnano/5.1.14_postcss@8.4.21: + resolution: {integrity: sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-preset-default: 5.2.13_postcss@8.4.21 + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + dev: true + + /csso/4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + dependencies: + css-tree: 1.1.3 + dev: true + + /csstype/3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: true + + /damerau-levenshtein/1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dev: true + + /date-fns/2.29.3: + resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + engines: {node: '>=0.11'} + dev: true + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-equal/2.2.0: + resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} + dependencies: + call-bind: 1.0.2 + es-get-iterator: 1.1.3 + get-intrinsic: 1.1.3 + is-arguments: 1.1.1 + is-array-buffer: 3.0.1 + is-date-object: 1.0.5 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + isarray: 2.0.5 + object-is: 1.1.5 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.9 + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge/4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + engines: {node: '>=0.10.0'} + dev: true + + /define-properties/1.1.4: + resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /detect-node/2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + dev: true + + /diff-sequences/29.0.0: + resolution: {integrity: sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-serializer/1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: true + + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: true + + /domhandler/4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domutils/2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: true + + /eastasianwidth/0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /ejs/3.1.8: + resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.5 + dev: true + + /electron-to-chromium/1.4.276: + resolution: {integrity: sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==} + dev: true + + /emoji-regex/8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex/9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /entities/2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract/1.20.4: + resolution: {integrity: sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.3 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 + dev: true + + /es-get-iterator/1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.2 + is-set: 2.0.2 + is-string: 1.0.7 + isarray: 2.0.5 + stop-iteration-iterator: 1.0.0 + dev: true + + /es-shim-unscopables/1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild/0.16.17: + resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.17 + '@esbuild/android-arm64': 0.16.17 + '@esbuild/android-x64': 0.16.17 + '@esbuild/darwin-arm64': 0.16.17 + '@esbuild/darwin-x64': 0.16.17 + '@esbuild/freebsd-arm64': 0.16.17 + '@esbuild/freebsd-x64': 0.16.17 + '@esbuild/linux-arm': 0.16.17 + '@esbuild/linux-arm64': 0.16.17 + '@esbuild/linux-ia32': 0.16.17 + '@esbuild/linux-loong64': 0.16.17 + '@esbuild/linux-mips64el': 0.16.17 + '@esbuild/linux-ppc64': 0.16.17 + '@esbuild/linux-riscv64': 0.16.17 + '@esbuild/linux-s390x': 0.16.17 + '@esbuild/linux-x64': 0.16.17 + '@esbuild/netbsd-x64': 0.16.17 + '@esbuild/openbsd-x64': 0.16.17 + '@esbuild/sunos-x64': 0.16.17 + '@esbuild/win32-arm64': 0.16.17 + '@esbuild/win32-ia32': 0.16.17 + '@esbuild/win32-x64': 0.16.17 + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp/2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-airbnb-base/15.0.0_ps7hf4l2dvbuxvtusmrfhmzsba: + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.32.0 + eslint-plugin-import: 2.27.5_6savw6y3b7jng6f64kgkyoij64 + object.assign: 4.1.4 + object.entries: 1.1.5 + semver: 6.3.0 + dev: true + + /eslint-config-prettier/8.6.0_eslint@8.32.0: + resolution: {integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.32.0 + dev: true + + /eslint-config-react-app/7.0.1_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==} + engines: {node: '>=14.0.0'} + peerDependencies: + eslint: ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.3 + '@babel/eslint-parser': 7.19.1_7zv64ewctsjhrlqbm3wep2xela + '@rushstack/eslint-patch': 1.2.0 + '@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si + '@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje + babel-preset-react-app: 10.0.1 + confusing-browser-globals: 1.0.11 + eslint: 8.32.0 + eslint-plugin-flowtype: 8.0.3_eslint@8.32.0 + eslint-plugin-import: 2.27.5_6savw6y3b7jng6f64kgkyoij64 + eslint-plugin-jest: 25.7.0_sa4tfo476gi7rdzbz5wa2vwvhe + eslint-plugin-jsx-a11y: 6.7.1_eslint@8.32.0 + eslint-plugin-react: 7.32.1_eslint@8.32.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.32.0 + eslint-plugin-testing-library: 5.7.2_7uibuqfxkfaozanbtbziikiqje + typescript: 4.9.4 + transitivePeerDependencies: + - '@babel/plugin-syntax-flow' + - '@babel/plugin-transform-react-jsx' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - jest + - supports-color + dev: true + + /eslint-import-resolver-node/0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + dependencies: + debug: 3.2.7 + is-core-module: 2.11.0 + resolve: 1.22.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils/2.7.4_cnxxylyx37asr43xy64ejg3pwe: + resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje + debug: 3.2.7 + eslint: 8.32.0 + eslint-import-resolver-node: 0.3.7 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-flowtype/8.0.3_eslint@8.32.0: + resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@babel/plugin-syntax-flow': ^7.14.5 + '@babel/plugin-transform-react-jsx': ^7.14.9 + eslint: ^8.1.0 + dependencies: + eslint: 8.32.0 + lodash: 4.17.21 + string-natural-compare: 3.0.1 + dev: true + + /eslint-plugin-import/2.27.5_6savw6y3b7jng6f64kgkyoij64: + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.32.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.7.4_cnxxylyx37asr43xy64ejg3pwe + has: 1.0.3 + is-core-module: 2.11.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.1 + semver: 6.3.0 + tsconfig-paths: 3.14.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jest/25.7.0_sa4tfo476gi7rdzbz5wa2vwvhe: + resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si + '@typescript-eslint/experimental-utils': 5.39.0_7uibuqfxkfaozanbtbziikiqje + eslint: 8.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-jest/27.2.1_sa4tfo476gi7rdzbz5wa2vwvhe: + resolution: {integrity: sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si + '@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje + eslint: 8.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-jsx-a11y/6.7.1_eslint@8.32.0: + resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + '@babel/runtime': 7.20.13 + aria-query: 5.1.3 + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + ast-types-flow: 0.0.7 + axe-core: 4.6.3 + axobject-query: 3.1.1 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 8.32.0 + has: 1.0.3 + jsx-ast-utils: 3.3.3 + language-tags: 1.0.5 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + semver: 6.3.0 + dev: true + + /eslint-plugin-react-hooks/4.6.0_eslint@8.32.0: + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.32.0 + dev: true + + /eslint-plugin-react/7.32.1_eslint@8.32.0: + resolution: {integrity: sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 + doctrine: 2.1.0 + eslint: 8.32.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.3 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.0 + string.prototype.matchall: 4.0.8 + dev: true + + /eslint-plugin-simple-import-sort/9.0.0_eslint@8.32.0: + resolution: {integrity: sha512-PtrLjyXP8kjRneWT1n0b99y/2Fyup37we7FVoWsI61/O7x4ztLohzhep/pxI/cYlECr/cQ2j6utckdvWpVwXNA==} + peerDependencies: + eslint: '>=5.0.0' + dependencies: + eslint: 8.32.0 + dev: true + + /eslint-plugin-testing-library/5.7.2_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-0ZmHeR/DUUgEzW8rwUBRWxuqntipDtpvxK0hymdHnLlABryJkzd+CAHr+XnISaVsTisZ5MLHp6nQF+8COHLLTA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje + eslint: 8.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.32.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.32.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.32.0: + resolution: {integrity: sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.32.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.0 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.19.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.1.5 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/9.4.0: + resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.0 + acorn-jsx: 5.3.2_acorn@8.8.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker/1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + dev: true + + /estree-walker/2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /execa/6.1.0: + resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 3.0.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /exenv/1.2.2: + resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==} + dev: false + + /expect/29.1.2: + resolution: {integrity: sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.1.2 + jest-get-type: 29.0.0 + jest-matcher-utils: 29.1.2 + jest-message-util: 29.1.2 + jest-util: 29.1.2 + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /filelist/1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.0 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /for-each/0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + + /framer-motion/8.5.3_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-CkQHhGlldJptI0SGjmjUzseXCzi0lljDkToE7cANHRCs12F5I84ymdWvCi6HfIKkt6Ykq2RiDd3dGuYV0oMn2w==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@motionone/dom': 10.15.5 + hey-listen: 1.0.8 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + tslib: 2.4.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + dev: true + + /fs-extra/9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-intrinsic/1.1.3: + resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-own-enumerable-property-symbols/3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + dev: true + + /get-stream/6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals/13.19.0: + resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd/1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.1.3 + dev: true + + /graceful-fs/4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /hamt_plus/1.0.2: + resolution: {integrity: sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==} + dev: true + + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.1.3 + dev: true + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /hey-listen/1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + dev: true + + /history/5.3.0: + resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} + dependencies: + '@babel/runtime': 7.20.13 + dev: false + + /html-parse-stringify/3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + dependencies: + void-elements: 3.1.0 + dev: true + + /human-signals/3.0.1: + resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} + engines: {node: '>=12.20.0'} + dev: true + + /husky/8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /i18next-browser-languagedetector/7.0.1: + resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} + dependencies: + '@babel/runtime': 7.20.13 + dev: true + + /i18next-http-backend/2.1.1: + resolution: {integrity: sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: true + + /i18next/22.4.9: + resolution: {integrity: sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==} + dependencies: + '@babel/runtime': 7.20.13 + dev: true + + /idb/7.1.0: + resolution: {integrity: sha512-Wsk07aAxDsntgYJY4h0knZJuTxM73eQ4reRAO+Z1liOh8eMCJ/MoDS8fCui1vGT9mnjtl1sOu3I2i/W1swPYZg==} + dev: true + + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /immer/9.0.18: + resolution: {integrity: sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==} + dev: true + + /immutable/4.1.0: + resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string/4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot/1.0.3: + resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /internal-slot/1.0.4: + resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /invariant/2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /is-arguments/1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-array-buffer/3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish/0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable/1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point/3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-fullwidth-code-point/4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-map/2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + + /is-module/1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + dev: true + + /is-negative-zero/2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-obj/1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-regexp/1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-set/2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream/2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream/3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array/1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-weakmap/2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + + /is-weakref/1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-weakset/2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + dev: true + + /isarray/2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jake/10.8.5: + resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.4 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jest-diff/29.1.2: + resolution: {integrity: sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.0.0 + jest-get-type: 29.0.0 + pretty-format: 29.1.2 + dev: true + + /jest-get-type/29.0.0: + resolution: {integrity: sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-matcher-utils/29.1.2: + resolution: {integrity: sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.1.2 + jest-get-type: 29.0.0 + pretty-format: 29.1.2 + dev: true + + /jest-message-util/29.1.2: + resolution: {integrity: sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 29.1.2 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 29.1.2 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + + /jest-util/29.1.2: + resolution: {integrity: sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.1.2 + '@types/node': 18.8.3 + chalk: 4.1.2 + ci-info: 3.5.0 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-worker/26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 18.8.3 + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + + /js-sdsl/4.1.5: + resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} + dev: true + + /js-sha3/0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: true + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc/0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-parse-even-better-errors/2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse/1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-schema/0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5/1.0.1: + resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + hasBin: true + dependencies: + minimist: 1.2.6 + dev: true + + /json5/2.2.1: + resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /json5/2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonfile/6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + + /jsonpointer/5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: true + + /jsx-ast-utils/3.3.3: + resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.6 + object.assign: 4.1.4 + dev: true + + /language-subtag-registry/0.3.22: + resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + dev: true + + /language-tags/1.0.5: + resolution: {integrity: sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==} + dependencies: + language-subtag-registry: 0.3.22 + dev: true + + /leven/3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns/1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /lint-staged/13.1.0: + resolution: {integrity: sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + cli-truncate: 3.1.0 + colorette: 2.0.19 + commander: 9.5.0 + debug: 4.3.4 + execa: 6.1.0 + lilconfig: 2.0.6 + listr2: 5.0.7 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-inspect: 1.12.2 + pidtree: 0.6.0 + string-argv: 0.3.1 + yaml: 2.2.1 + transitivePeerDependencies: + - enquirer + - supports-color + dev: true + + /listr2/5.0.7: + resolution: {integrity: sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.8.0 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash.debounce/4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: true + + /lodash.memoize/4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.sortby/4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true + + /lodash.uniq/4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + dev: true + + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-update/4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /lru-cache/5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string/0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /match-sorter/6.3.1: + resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==} + dependencies: + '@babel/runtime': 7.20.13 + remove-accents: 0.4.2 + dev: true + + /mdn-data/2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: true + + /memoize-one/5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + dev: false + + /memoize-one/6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + dev: false + + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /microseconds/0.2.0: + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} + dev: true + + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn/4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch/5.1.0: + resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist/1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + dev: true + + /modern-normalize/1.1.0: + resolution: {integrity: sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==} + engines: {node: '>=6'} + dev: false + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nano-time/1.0.0: + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} + dependencies: + big-integer: 1.6.51 + dev: true + + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare-lite/1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-fetch/2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-url/6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: true + + /npm-run-path/5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nth-check/2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: true + + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-inspect/1.12.2: + resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + dev: true + + /object-is/1.1.5: + resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + dev: true + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign/4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries/1.1.5: + resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /object.entries/1.1.6: + resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /object.fromentries/2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /object.hasown/1.1.2: + resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + dependencies: + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /object.values/1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /oblivious-set/1.0.0: + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime/6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-map/4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json/5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.18.6 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key/4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pidtree/0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /postcss-attribute-case-insensitive/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-Bi5tVYe9rKjU1k1v9xzAAZqMzUrlb2sVbRyV7ZDdtLJVpxV+6db5Yd6ESe6CMl09brexIfDUrGPVB1IbVc7bQQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-calc/8.2.4_postcss@8.4.21: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-clamp/4.1.0_postcss@8.4.21: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-functional-notation/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-oxsPSdf6i2DJyNj24/hvQDaTyoztDwG0TMannHGUnFAdA6xSwB8Io5iDDqZcDrXTJTy32erpG9ETmwhMPPuCNg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-hex-alpha/9.0.0_postcss@8.4.21: + resolution: {integrity: sha512-NdIN7IzYadBApUm+wULJR03746sKliIrmRIdjHF6GaUCztsDdcq4L9wtYz5X3mw/t08mjZx8ld5By6StB+1lLQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-color-rebeccapurple/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-K0PT/ZNEYva+jIlueaI8OUPL59SfPThflYe/htggUKaS6ydiimPmtvrFb8OsTJHcQ6QUPcIySTZdFNmdnEITQg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-colormin/5.3.0_postcss@8.4.21: + resolution: {integrity: sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-convert-values/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-custom-media/9.1.0_postcss@8.4.21: + resolution: {integrity: sha512-K9sIQhdsXazHyhHaMIL/wztFV6ABHi6NwxNPO3q0o0T2zkI4oEqI1TjeoncBKIY6xPrqnWTV40KF8AJ7yd0W6g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.0_jhntdqzgrqlkpxki6fy253alji + '@csstools/css-parser-algorithms': 2.0.0_wcbxa2wg3evugsqpd27xwuyb6a + '@csstools/css-tokenizer': 2.0.0 + '@csstools/media-query-list-parser': 2.0.0_jhntdqzgrqlkpxki6fy253alji + postcss: 8.4.21 + dev: true + + /postcss-custom-properties/13.1.0_postcss@8.4.21: + resolution: {integrity: sha512-O0Lg0CuHwADctEMBgGtaeams7eFES8pXo/9zBClTbRVdU3LFAkFluw1l9eYnJ3rtidp80EGbAIuiisEIu1Z+uA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.0_jhntdqzgrqlkpxki6fy253alji + '@csstools/css-parser-algorithms': 2.0.0_wcbxa2wg3evugsqpd27xwuyb6a + '@csstools/css-tokenizer': 2.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-custom-selectors/7.1.0_postcss@8.4.21: + resolution: {integrity: sha512-83a8lfR+3tWotHDGGPSadPB0oqBieqi62EhdBe7Qo+eW/aEst7xjq2fXH+dUy8KVEFcM3jobXMYJnSo6omcVHA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/cascade-layer-name-parser': 1.0.0_jhntdqzgrqlkpxki6fy253alji + '@csstools/css-parser-algorithms': 2.0.0_wcbxa2wg3evugsqpd27xwuyb6a + '@csstools/css-tokenizer': 2.0.0 + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-dir-pseudo-class/7.0.0_postcss@8.4.21: + resolution: {integrity: sha512-i8I6vqB0T0fpanLBjFoMPp3iTgKPccZCyZ149Q1RuRVlnKD00DbRFSkbp4/XDJaNzKJeto/DM/Uj62icEtVh9A==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-discard-comments/5.1.2_postcss@8.4.21: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-duplicates/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-empty/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-overridden/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-double-position-gradients/4.0.0_postcss@8.4.21: + resolution: {integrity: sha512-RTOs3chf/D1ETvaT+BcdGkPmRxoImZn3hmfAkFGET5ijx3Lnw7npubQDvwqOL1on54/uN6w9BGuEMSUsOK+2kA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-focus-visible/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-mLIYkOFGXJSJtFwj9VX5LYUfWbxuwBWQxOqcdKk2p4apCAvsxji0jFpZhOPEpwD9YFbcTi0RWb+9Zam5y7qw5g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-focus-within/7.0.0_postcss@8.4.21: + resolution: {integrity: sha512-DdIAwUY/7D981ROrWjxDuhCOTlbgjqbn2lCCuHWcGm+4s3m7thOCQzBGWBFc1heIx3MkiG1qF4Ew4logiPOxaQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-font-variant/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-gap-properties/4.0.0_postcss@8.4.21: + resolution: {integrity: sha512-ACrVEX+DZRkFNImiRBiFw56BW7OY43F/0AjusgBxGNE0mLvfqINkYQT421YMHB7HGs+rekladOcBNmYZu6+/iQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-image-set-function/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-a7q8XdGnU4OMnBqRzVkUBTtuRztD4YIy0b+52OxAcBqvhOU39A4ego9fUpVaqqrQrDOVuXmh/MYwC82aYuJkfQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-initial/4.0.1_postcss@8.4.21: + resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-lab-function/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-XTV77sdIJGPxDYzZxXE0giTn3mQDC/sl/a9i2VVOPdVEEK7wFbd3kM9Dom20F4WtioTFllpDl3oMBoQvCrl79w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-logical/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-pn50jY5c+PmpYiTZ7KfYQ4aKXAVaFfZgNevtUwXglD22TxfLrrYD5d8m7UDQkT9CAfYvBgSkzPSBWTyE0WuQmA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-media-minmax/5.0.0_postcss@8.4.21: + resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.1.0 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-merge-longhand/5.1.7_postcss@8.4.21: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1_postcss@8.4.21 + dev: true + + /postcss-merge-rules/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-minify-font-values/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-gradients/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-params/5.1.4_postcss@8.4.21: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-selectors/5.2.1_postcss@8.4.21: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-nesting/11.0.0_postcss@8.4.21: + resolution: {integrity: sha512-Y+jmDpQuSSoM/Qq+rqDc4D3E8Cn84qUmJLFS/M5u0YgM+5adLi9qFApbz5XzjzXjGAzItTUCP7RikLGy06ebiA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/selector-specificity': 2.1.0_jwkxkvlpbeeukrxssifiwvrjeq + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-normalize-charset/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-normalize-display-values/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-positions/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-repeat-style/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-string/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-timing-functions/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-unicode/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-url/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + normalize-url: 6.1.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-whitespace/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-opacity-percentage/1.1.3_postcss@8.4.21: + resolution: {integrity: sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-ordered-values/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-overflow-shorthand/4.0.0_postcss@8.4.21: + resolution: {integrity: sha512-HJ+HIX6IeVyDBu+b5cJScwGYPnGokswXWH1izVgJGT6D5mrHzQJUvWWvHBRlopCAEbtose+JNOjRQgTRGHjm3A==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-page-break/3.0.4_postcss@8.4.21: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-place/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-LFAiOWgekmvJ8/o4Cl1pPTuMryoMkr8PqAgv4j8i7iB9quinOhG4TXaq8RpF4nNIb9qOvn6+6LX4/BLcnza3rQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-preset-env/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-/kFWdq109OONR2Hl3T3nmo1dOdS7lHB6kF/nPaBn/2lGjQ99f/j5vGBYvupSmSni+F7T/0A0Xb0nfbJLnxwdjA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + '@csstools/postcss-cascade-layers': 3.0.0_postcss@8.4.21 + '@csstools/postcss-color-function': 2.0.0_postcss@8.4.21 + '@csstools/postcss-font-format-keywords': 2.0.0_postcss@8.4.21 + '@csstools/postcss-hwb-function': 2.0.0_postcss@8.4.21 + '@csstools/postcss-ic-unit': 2.0.0_postcss@8.4.21 + '@csstools/postcss-is-pseudo-class': 3.0.0_postcss@8.4.21 + '@csstools/postcss-logical-float-and-clear': 1.0.0_postcss@8.4.21 + '@csstools/postcss-logical-resize': 1.0.0_postcss@8.4.21 + '@csstools/postcss-logical-viewport-units': 1.0.1_postcss@8.4.21 + '@csstools/postcss-media-queries-aspect-ratio-number-values': 1.0.0_postcss@8.4.21 + '@csstools/postcss-nested-calc': 2.0.0_postcss@8.4.21 + '@csstools/postcss-normalize-display-values': 2.0.0_postcss@8.4.21 + '@csstools/postcss-oklab-function': 2.0.0_postcss@8.4.21 + '@csstools/postcss-progressive-custom-properties': 2.0.0_postcss@8.4.21 + '@csstools/postcss-scope-pseudo-class': 2.0.0_postcss@8.4.21 + '@csstools/postcss-stepped-value-functions': 2.0.0_postcss@8.4.21 + '@csstools/postcss-text-decoration-shorthand': 2.0.0_postcss@8.4.21 + '@csstools/postcss-trigonometric-functions': 2.0.0_postcss@8.4.21 + '@csstools/postcss-unset-value': 2.0.0_postcss@8.4.21 + autoprefixer: 10.4.13_postcss@8.4.21 + browserslist: 4.21.4 + css-blank-pseudo: 5.0.0_postcss@8.4.21 + css-has-pseudo: 5.0.0_postcss@8.4.21 + css-prefers-color-scheme: 8.0.0_postcss@8.4.21 + cssdb: 7.4.1 + postcss: 8.4.21 + postcss-attribute-case-insensitive: 6.0.0_postcss@8.4.21 + postcss-clamp: 4.1.0_postcss@8.4.21 + postcss-color-functional-notation: 5.0.0_postcss@8.4.21 + postcss-color-hex-alpha: 9.0.0_postcss@8.4.21 + postcss-color-rebeccapurple: 8.0.0_postcss@8.4.21 + postcss-custom-media: 9.1.0_postcss@8.4.21 + postcss-custom-properties: 13.1.0_postcss@8.4.21 + postcss-custom-selectors: 7.1.0_postcss@8.4.21 + postcss-dir-pseudo-class: 7.0.0_postcss@8.4.21 + postcss-double-position-gradients: 4.0.0_postcss@8.4.21 + postcss-focus-visible: 8.0.0_postcss@8.4.21 + postcss-focus-within: 7.0.0_postcss@8.4.21 + postcss-font-variant: 5.0.0_postcss@8.4.21 + postcss-gap-properties: 4.0.0_postcss@8.4.21 + postcss-image-set-function: 5.0.0_postcss@8.4.21 + postcss-initial: 4.0.1_postcss@8.4.21 + postcss-lab-function: 5.0.0_postcss@8.4.21 + postcss-logical: 6.0.0_postcss@8.4.21 + postcss-media-minmax: 5.0.0_postcss@8.4.21 + postcss-nesting: 11.0.0_postcss@8.4.21 + postcss-opacity-percentage: 1.1.3_postcss@8.4.21 + postcss-overflow-shorthand: 4.0.0_postcss@8.4.21 + postcss-page-break: 3.0.4_postcss@8.4.21 + postcss-place: 8.0.0_postcss@8.4.21 + postcss-pseudo-class-any-link: 8.0.0_postcss@8.4.21 + postcss-replace-overflow-wrap: 4.0.0_postcss@8.4.21 + postcss-selector-not: 7.0.0_postcss@8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-pseudo-class-any-link/8.0.0_postcss@8.4.21: + resolution: {integrity: sha512-9+SUrDDrmyQijQBRSZFfx5eL0N9sdtHhibcGPgmyQyYCshFZbhH22vfbo2z84U2TI8kh1TrN86t5N2xN2ojq0w==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-reduce-initial/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + caniuse-api: 3.0.0 + postcss: 8.4.21 + dev: true + + /postcss-reduce-transforms/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-replace-overflow-wrap/4.0.0_postcss@8.4.21: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-selector-not/7.0.0_postcss@8.4.21: + resolution: {integrity: sha512-vYYltgqe8JXLW/oWuENL6mKc7zbJWDA86kwQgGzJsalkvPOPcaM+G90FqjEiGllRAXIv3WmgehtQEfIJUDlUhg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-selector-parser/6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-svgo/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + dev: true + + /postcss-unique-selectors/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss/8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier/2.8.3: + resolution: {integrity: sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-bytes/5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + dev: true + + /pretty-bytes/6.0.0: + resolution: {integrity: sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + + /pretty-format/29.1.2: + resolution: {integrity: sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prop-types/15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /react-dom/18.2.0_react@18.2.0: + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + + /react-feather/2.0.10_react@18.2.0: + resolution: {integrity: sha512-BLhukwJ+Z92Nmdcs+EMw6dy1Z/VLiJTzEQACDUEnWMClhYnFykJCGWQx+NmwP/qQHGX/5CzQ+TGi8ofg2+HzVQ==} + peerDependencies: + react: '>=16.8.6' + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + dev: false + + /react-i18next/12.1.4_iakk3dtjhjpukdoa4oua5khgci: + resolution: {integrity: sha512-XQND7jYtgM7ht5PH3yIZljCRpAMTlH/zmngM9ZjToqa+0BR6xuu8c7QF0WIIOEjcMTB2S3iOfpN/xG/ZrAnO6g==} + peerDependencies: + i18next: '>= 19.0.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.20.13 + html-parse-stringify: 3.0.1 + i18next: 22.4.9 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: true + + /react-icons/4.7.1_react@18.2.0: + resolution: {integrity: sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: true + + /react-is/16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-is/18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react-lifecycles-compat/3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + + /react-modal/3.16.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==} + engines: {node: '>=8'} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 + react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 + dependencies: + exenv: 1.2.2 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-lifecycles-compat: 3.0.4 + warning: 4.0.3 + dev: false + + /react-query/3.39.3_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.20.13 + broadcast-channel: 3.7.0 + match-sorter: 6.3.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: true + + /react-refresh/0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-router-dom/6.8.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-hQouduSTywGJndE86CXJ2h7YEy4HYC6C/uh19etM+79FfQ6cFFFHnHyDlzO4Pq0eBUI96E4qVE5yUjA00yJZGQ==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.3.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-router: 6.8.0_react@18.2.0 + dev: true + + /react-router/6.8.0_react@18.2.0: + resolution: {integrity: sha512-760bk7y3QwabduExtudhWbd88IBbuD1YfwzpuDUAlJUJ7laIIcqhMvdhSVh1Fur1PE8cGl84L0dxhR3/gvHF7A==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.3.1 + react: 18.2.0 + dev: true + + /react-switch/7.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA==} + peerDependencies: + react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: true + + /react-table/7.8.0_react@18.2.0: + resolution: {integrity: sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==} + peerDependencies: + react: ^16.8.3 || ^17.0.0-0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: true + + /react-tabs/6.0.0_react@18.2.0: + resolution: {integrity: sha512-8jKLKrlwxmn5/+xsa76yi27ZdB8E/WhlhQZw739O5UlOeUGtVoVeWnpqIewv/KbjTw7gQf/uA51zWUNt4IVygQ==} + peerDependencies: + react: ^18.0.0 + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + + /react-tiny-fab/4.0.4_react@18.2.0: + resolution: {integrity: sha512-PxT6gEnIQR2vFfeIaa1Oq4PRX+cIEDbEfbS6PyevWCQngrKfqjMKPEcZOaaURaUclB9u3RilgjkaBUQFVlbWcg==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16.8' + dependencies: + react: 18.2.0 + dev: false + + /react-window/1.8.8_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.20.13 + memoize-one: 5.2.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /react/18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /recoil/0.7.6_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-hsBEw7jFdpBCY/tu2GweiyaqHKxVj6EqF2/SfrglbKvJHhpN57SANWvPW+gE90i3Awi+A5gssOd3u+vWlT+g7g==} + peerDependencies: + react: '>=16.13.1' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + hamt_plus: 1.0.2 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: true + + /regenerate-unicode-properties/10.1.0: + resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + dev: true + + /regenerate/1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + dev: true + + /regenerator-runtime/0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + /regenerator-transform/0.15.0: + resolution: {integrity: sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==} + dependencies: + '@babel/runtime': 7.20.13 + dev: true + + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /regexpu-core/5.2.1: + resolution: {integrity: sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.0 + regjsgen: 0.7.1 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.0.0 + dev: true + + /regjsgen/0.7.1: + resolution: {integrity: sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==} + dev: true + + /regjsparser/0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /remove-accents/0.4.2: + resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} + dev: true + + /require-from-string/2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /reselect/4.1.7: + resolution: {integrity: sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==} + dev: true + + /resize-observer-polyfill/1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve/2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor/3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rfdc/1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup-plugin-terser/7.0.2_rollup@2.79.1: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: ^2.0.0 + dependencies: + '@babel/code-frame': 7.18.6 + jest-worker: 26.6.2 + rollup: 2.79.1 + serialize-javascript: 4.0.0 + terser: 5.15.1 + dev: true + + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /rollup/3.11.0: + resolution: {integrity: sha512-+uWPPkpWQ2H3Qi7sNBcRfhhHJyUNgBYhG4wKe5wuGRj2m55kpo+0p5jubKNBjQODyPe6tSBE3tNpdDwEisQvAQ==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs/7.8.0: + resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} + dependencies: + tslib: 2.4.1 + dev: true + + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test/1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-regex: 1.1.4 + dev: true + + /sass/1.57.1: + resolution: {integrity: sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.1.0 + source-map-js: 1.0.2 + dev: true + + /scheduler/0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serialize-javascript/4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + dependencies: + randombytes: 2.1.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + object-inspect: 1.12.2 + dev: true + + /signal-exit/3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi/3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-support/0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map/0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + /stable/0.1.8: + resolution: {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 + + /stack-utils/2.0.5: + resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /stop-iteration-iterator/1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + dependencies: + internal-slot: 1.0.4 + dev: true + + /string-argv/0.3.1: + resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} + engines: {node: '>=0.6.19'} + dev: true + + /string-natural-compare/3.0.1: + resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} + dev: true + + /string-width/4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width/5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.0.1 + dev: true + + /string.prototype.matchall/4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + get-intrinsic: 1.1.3 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + dev: true + + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /stringify-object/3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi/7.0.1: + resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-comments/2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + dev: true + + /strip-final-newline/3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /stylehacks/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.4 + postcss: 8.4.21 + postcss-selector-parser: 6.0.10 + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svgo/2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.0 + stable: 0.1.8 + dev: true + + /temp-dir/2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + dev: true + + /tempy/0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + dev: true + + /terser/5.15.1: + resolution: {integrity: sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.2 + acorn: 8.8.0 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through/2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /to-fast-properties/2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tr46/0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true + + /tr46/1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.1.1 + dev: true + + /tsconfig-paths/3.14.1: + resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.6 + strip-bom: 3.0.0 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + + /tsutils/3.21.0_typescript@4.9.4: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.9.4 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /typescript/4.9.4: + resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /unicode-canonical-property-names-ecmascript/2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + dev: true + + /unicode-match-property-ecmascript/2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + dev: true + + /unicode-match-property-value-ecmascript/2.0.0: + resolution: {integrity: sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==} + engines: {node: '>=4'} + dev: true + + /unicode-property-aliases-ecmascript/2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + dev: true + + /unique-string/2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: true + + /universalify/2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /unload/2.2.0: + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} + dependencies: + '@babel/runtime': 7.20.13 + detect-node: 2.1.0 + dev: true + + /upath/1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + dev: true + + /update-browserslist-db/1.0.10_browserslist@4.21.4: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.4 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /use-asset/1.0.4_react@18.2.0: + resolution: {integrity: sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA==} + peerDependencies: + react: '>=17.0' + dependencies: + fast-deep-equal: 3.1.3 + react: 18.2.0 + dev: false + + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite-plugin-pwa/0.14.1_vite@4.0.4: + resolution: {integrity: sha512-5zx7yhQ8RTLwV71+GA9YsQQ63ALKG8XXIMqRJDdZkR8ZYftFcRgnzM7wOWmQZ/DATspyhPih5wCdcZnAIsM+mA==} + peerDependencies: + vite: ^3.1.0 || ^4.0.0 + dependencies: + '@rollup/plugin-replace': 5.0.2_rollup@3.11.0 + debug: 4.3.4 + fast-glob: 3.2.12 + pretty-bytes: 6.0.0 + rollup: 3.11.0 + vite: 4.0.4_sass@1.57.1 + workbox-build: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + + /vite/4.0.4_sass@1.57.1: + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.16.17 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.11.0 + sass: 1.57.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /void-elements/3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + dev: true + + /warning/4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /webidl-conversions/3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + + /webidl-conversions/4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + + /whatwg-url/5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: true + + /whatwg-url/7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-collection/1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + + /which-typed-array/1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /workbox-background-sync/6.5.4: + resolution: {integrity: sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==} + dependencies: + idb: 7.1.0 + workbox-core: 6.5.4 + dev: true + + /workbox-broadcast-update/6.5.4: + resolution: {integrity: sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-build/6.5.4: + resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==} + engines: {node: '>=10.0.0'} + dependencies: + '@apideck/better-ajv-errors': 0.3.6_ajv@8.11.0 + '@babel/core': 7.20.12 + '@babel/preset-env': 7.19.4_@babel+core@7.20.12 + '@babel/runtime': 7.20.13 + '@rollup/plugin-babel': 5.3.1_3dsfpkpoyvuuxyfgdbpn4j4uzm + '@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1 + '@rollup/plugin-replace': 2.4.2_rollup@2.79.1 + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.11.0 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.1 + rollup-plugin-terser: 7.0.2_rollup@2.79.1 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 6.5.4 + workbox-broadcast-update: 6.5.4 + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-google-analytics: 6.5.4 + workbox-navigation-preload: 6.5.4 + workbox-precaching: 6.5.4 + workbox-range-requests: 6.5.4 + workbox-recipes: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + workbox-streams: 6.5.4 + workbox-sw: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + + /workbox-cacheable-response/6.5.4: + resolution: {integrity: sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-core/6.5.4: + resolution: {integrity: sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==} + dev: true + + /workbox-expiration/6.5.4: + resolution: {integrity: sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==} + dependencies: + idb: 7.1.0 + workbox-core: 6.5.4 + dev: true + + /workbox-google-analytics/6.5.4: + resolution: {integrity: sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==} + dependencies: + workbox-background-sync: 6.5.4 + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-navigation-preload/6.5.4: + resolution: {integrity: sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-precaching/6.5.4: + resolution: {integrity: sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-range-requests/6.5.4: + resolution: {integrity: sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-recipes/6.5.4: + resolution: {integrity: sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==} + dependencies: + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-precaching: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-routing/6.5.4: + resolution: {integrity: sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-strategies/6.5.4: + resolution: {integrity: sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-streams/6.5.4: + resolution: {integrity: sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + dev: true + + /workbox-sw/6.5.4: + resolution: {integrity: sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==} + dev: true + + /workbox-window/6.5.4: + resolution: {integrity: sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==} + dependencies: + '@types/trusted-types': 2.0.2 + workbox-core: 6.5.4 + dev: true + + /wrap-ansi/6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi/7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist/3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yaml/2.2.1: + resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} + engines: {node: '>= 14'} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..68f3713 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +module.exports = { + plugins: [require('postcss-preset-env')()], +}; diff --git a/src/App.module.scss b/src/App.module.scss new file mode 100644 index 0000000..e67057f --- /dev/null +++ b/src/App.module.scss @@ -0,0 +1,20 @@ +.app { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + + display: flex; + background: var(--color-background); + color: var(--color-text); + + @media (max-width: 768px) { + flex-direction: column; + } +} + +.content { + flex-grow: 1; + overflow-y: auto; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..7023bc9 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { QueryClientProvider } from 'react-query'; +import { HashRouter as Router, Route, RouteObject, Routes, useRoutes } from 'react-router-dom'; +import { RecoilRoot } from 'recoil'; + +import APIConfig from '~/components//APIConfig'; +import { About } from '~/components/about/About'; +import APIDiscovery from '~/components/APIDiscovery'; +import ErrorBoundary from '~/components/ErrorBoundary'; +import Home from '~/components/Home'; +import Loading from '~/components/Loading'; +import Loading2 from '~/components/Loading2'; +import { Head } from '~/components/shared/Head'; +import SideBar from '~/components/SideBar'; +import StateProvider from '~/components/StateProvider'; +import StyleGuide from '~/components/StyleGuide'; +import { queryClient } from '~/misc/query'; +import { actions, initialState } from '~/store'; + +import styles from './App.module.scss'; + +const { lazy, Suspense } = React; + +const Connections = lazy(() => import('~/components/Connections')); +const Config = lazy(() => import('~/components/Config')); +const Logs = lazy(() => import('~/components/Logs')); +const Proxies = lazy(() => import('~/components/proxies/Proxies')); +const Rules = lazy(() => import('~/components/Rules')); + +const routes = [ + { path: '/', element: }, + { path: '/connections', element: }, + { path: '/configs', element: }, + { path: '/logs', element: }, + { path: '/proxies', element: }, + { path: '/rules', element: }, + { path: '/about', element: }, + process.env.NODE_ENV === 'development' ? { path: '/style', element: } : false, +].filter(Boolean) as RouteObject[]; + +function SideBarApp() { + return ( + <> + + +
+ }>{useRoutes(routes)} +
+ + ); +} + +const App = () => ( + + + + +
+ + }> + + + } /> + } /> + + + +
+
+
+
+
+); + +export default App; diff --git a/src/api/configs.ts b/src/api/configs.ts new file mode 100644 index 0000000..ff62ce7 --- /dev/null +++ b/src/api/configs.ts @@ -0,0 +1,48 @@ +import { getURLAndInit } from '~/misc/request-helper'; +import { ClashGeneralConfig, TunPartial } from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +const endpoint = '/configs'; +const updateGeoDatabasesFileEndpoint = '/configs/geo'; +const flushFakeIPPoolEndpoint = '/cache/fakeip/flush'; + +export async function fetchConfigs(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + return await fetch(url + endpoint, init); +} + +// TODO support PUT /configs +// req body +// { Path: string } + +type ClashConfigPartial = TunPartial; +function configsPatchWorkaround(o: ClashConfigPartial) { + // backward compatibility for older clash using `socket-port` + if ('socks-port' in o) { + o['socket-port'] = o['socks-port']; + } + return o; +} + +export async function updateConfigs(apiConfig: ClashAPIConfig, o: ClashConfigPartial) { + const { url, init } = getURLAndInit(apiConfig); + const body = JSON.stringify(configsPatchWorkaround(o)); + return await fetch(url + endpoint, { ...init, body, method: 'PATCH' }); +} + +export async function reloadConfigFile(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + const body = '{"path": "", "payload": ""}'; + return await fetch(url + endpoint + '?force=true', { ...init, body, method: 'PUT' }); +} + +export async function updateGeoDatabasesFile(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + const body = '{"path": "", "payload": ""}'; + return await fetch(url + updateGeoDatabasesFileEndpoint, { ...init, body, method: 'POST' }); +} + +export async function flushFakeIPPool(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + return await fetch(url + flushFakeIPPoolEndpoint, { ...init, method: 'POST' }); +} diff --git a/src/api/connections.ts b/src/api/connections.ts new file mode 100644 index 0000000..7f27ebf --- /dev/null +++ b/src/api/connections.ts @@ -0,0 +1,91 @@ +import { ClashAPIConfig } from '~/types'; + +import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper'; + +const endpoint = '/connections'; + +const fetched = false; +const subscribers = []; + +// see also https://github.com/Dreamacro/clash/blob/dev/constant/metadata.go#L41 +type UUID = string; +type ConnNetwork = 'tcp' | 'udp'; +type ConnType = 'HTTP' | 'HTTP Connect' | 'Socks5' | 'Redir' | 'Unknown'; +export type ConnectionItem = { + id: UUID; + metadata: { + network: ConnNetwork; + type: ConnType; + sourceIP: string; + destinationIP: string; + remoteDestination: string; + sourcePort: string; + destinationPort: string; + host: string; + process?: string; + sniffHost?: string; + }; + upload: number; + download: number; + // e.g. "2019-11-30T22:48:13.416668+08:00", + start: string; + chains: string[]; + // e.g. 'Match', 'DomainKeyword' + rule: string; + rulePayload?: string; +}; +type ConnectionsData = { + downloadTotal: number; + uploadTotal: number; + connections: Array; +}; + +function appendData(s: string) { + let o: ConnectionsData; + try { + o = JSON.parse(s); + } catch (err) { + // eslint-disable-next-line no-console + console.log('JSON.parse error', JSON.parse(s)); + } + subscribers.forEach((f) => f(o)); +} + +type UnsubscribeFn = () => void; + +let wsState: number; +export function fetchData(apiConfig: ClashAPIConfig, listener: unknown): UnsubscribeFn | void { + if (fetched || wsState === 1) { + if (listener) return subscribe(listener); + } + wsState = 1; + const url = buildWebSocketURL(apiConfig, endpoint); + const ws = new WebSocket(url); + ws.addEventListener('error', () => (wsState = 3)); + ws.addEventListener('message', (event) => appendData(event.data)); + if (listener) return subscribe(listener); +} + +function subscribe(listener: unknown): UnsubscribeFn { + subscribers.push(listener); + return function unsubscribe() { + const idx = subscribers.indexOf(listener); + subscribers.splice(idx, 1); + }; +} + +export async function closeAllConnections(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + return await fetch(url + endpoint, { ...init, method: 'DELETE' }); +} + +export async function fetchConns(apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + return await fetch(url + endpoint, { ...init }); +} + +export async function closeConnById(apiConfig: ClashAPIConfig, id: string) { + const { url: baseURL, init } = getURLAndInit(apiConfig); + const url = `${baseURL}${endpoint}/${id}`; + return await fetch(url, { ...init, method: 'DELETE' }); +} diff --git a/src/api/logs.ts b/src/api/logs.ts new file mode 100644 index 0000000..db37bcd --- /dev/null +++ b/src/api/logs.ts @@ -0,0 +1,151 @@ +import { pad0 } from '~/misc/utils'; +import { Log } from '~/store/types'; +import { LogsAPIConfig } from '~/types'; + +import { buildLogsWebSocketURL, getURLAndInit } from '../misc/request-helper'; + +type AppendLogFn = (x: Log) => void; +enum WebSocketReadyState { + Connecting = 0, + Open = 1, + Closing = 2, + Closed = 3, +} + +const endpoint = '/logs'; +const textDecoder = new TextDecoder('utf-8'); + +const getRandomStr = () => { + return Math.floor((1 + Math.random()) * 0x10000).toString(16); +}; + +let even = false; +let fetched = false; +let decoded = ''; +let ws: WebSocket; +let prevAppendLogFn: AppendLogFn; + +function appendData(s: string, callback: AppendLogFn) { + let o: Partial; + try { + o = JSON.parse(s); + } catch (err) { + // eslint-disable-next-line no-console + console.log('JSON.parse error', JSON.parse(s)); + } + + const now = new Date(); + const time = formatDate(now); + // mutate input param in place intentionally + o.time = time; + o.id = +now - 0 + getRandomStr(); + o.even = even = !even; + callback(o as Log); +} + +function formatDate(d: Date) { + // 19-03-09 12:45 + const YY = d.getFullYear() % 100; + const MM = pad0(d.getMonth() + 1, 2); + const dd = pad0(d.getDate(), 2); + const HH = pad0(d.getHours(), 2); + const mm = pad0(d.getMinutes(), 2); + const ss = pad0(d.getSeconds(), 2); + return `${YY}-${MM}-${dd} ${HH}:${mm}:${ss}`; +} + +function pump(reader: ReadableStreamDefaultReader, appendLog: AppendLogFn) { + return reader.read().then(({ done, value }) => { + const str = textDecoder.decode(value, { stream: !done }); + decoded += str; + + const splits = decoded.split('\n'); + + const lastSplit = splits[splits.length - 1]; + + for (let i = 0; i < splits.length - 1; i++) { + appendData(splits[i], appendLog); + } + + if (done) { + appendData(lastSplit, appendLog); + decoded = ''; + + // eslint-disable-next-line no-console + console.log('GET /logs streaming done'); + fetched = false; + return; + } else { + decoded = lastSplit; + } + return pump(reader, appendLog); + }); +} + +/** loose hashing of the connection configuration */ +function makeConnStr(c: LogsAPIConfig) { + const keys = Object.keys(c); + keys.sort(); + return keys.map((k) => c[k]).join('|'); +} + +let prevConnStr: string; +let controller: AbortController; + +export function fetchLogs(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { + if (apiConfig.logLevel === 'uninit') return; + if (fetched || (ws && ws.readyState === WebSocketReadyState.Open)) return; + prevAppendLogFn = appendLog; + const url = buildLogsWebSocketURL(apiConfig, endpoint); + ws = new WebSocket(url); + ws.addEventListener('error', () => { + fetchLogsWithFetch(apiConfig, appendLog); + }); + ws.addEventListener('message', function (event) { + appendData(event.data, appendLog); + }); +} + +export function stop() { + ws.close(); + if (controller) controller.abort(); +} + +export function reconnect(apiConfig: LogsAPIConfig) { + if (!prevAppendLogFn || !ws) return; + ws.close(); + fetched = false; + fetchLogs(apiConfig, prevAppendLogFn); +} + +function fetchLogsWithFetch(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { + if (controller && makeConnStr(apiConfig) !== prevConnStr) { + controller.abort(); + } else if (fetched) { + return; + } + + fetched = true; + prevConnStr = makeConnStr(apiConfig); + + controller = new AbortController(); + const signal = controller.signal; + + const { url, init } = getURLAndInit(apiConfig); + fetch(url + endpoint + '?level=' + apiConfig.logLevel, { + ...init, + signal, + }).then( + (response) => { + const reader = response.body.getReader(); + pump(reader, appendLog); + }, + (err) => { + fetched = false; + if (signal.aborted) return; + + // eslint-disable-next-line no-console + console.log('GET /logs error:', err.message); + } + ); +} diff --git a/src/api/proxies.ts b/src/api/proxies.ts new file mode 100644 index 0000000..36aa05d --- /dev/null +++ b/src/api/proxies.ts @@ -0,0 +1,78 @@ +import { getURLAndInit } from '../misc/request-helper'; + +const endpoint = '/proxies'; + +/* +$ curl "http://127.0.0.1:8080/proxies/Proxy" -XPUT -d '{ "name": "ss3" }' -i +HTTP/1.1 400 Bad Request +Content-Type: text/plain; charset=utf-8 + +{"error":"Selector update error: Proxy does not exist"} + +~ +$ curl "http://127.0.0.1:8080/proxies/GLOBAL" -XPUT -d '{ "name": "Proxy" }' -i +HTTP/1.1 204 No Content +*/ + +export async function fetchProxies(config) { + const { url, init } = getURLAndInit(config); + const res = await fetch(url + endpoint, init); + return await res.json(); +} + +export async function requestToSwitchProxy(apiConfig, name1, name2) { + const body = { name: name2 }; + const { url, init } = getURLAndInit(apiConfig); + const fullURL = `${url}${endpoint}/${name1}`; + return await fetch(fullURL, { + ...init, + method: 'PUT', + body: JSON.stringify(body), + }); +} + +export async function requestDelayForProxy( + apiConfig, + name, + latencyTestUrl = 'http://www.gstatic.com/generate_204' +) { + const { url, init } = getURLAndInit(apiConfig); + const qs = `timeout=5000&url=${encodeURIComponent(latencyTestUrl)}`; + const fullURL = `${url}${endpoint}/${encodeURIComponent(name)}/delay?${qs}`; + return await fetch(fullURL, init); +} + +export async function requestDelayForProxyGroup( + apiConfig, + name, + latencyTestUrl = 'http://www.gstatic.com/generate_202' +) { + const { url, init } = getURLAndInit(apiConfig); + const qs = `url=${encodeURIComponent(latencyTestUrl)}&timeout=2000`; + const fullUrl = `${url}/group/${encodeURIComponent(name)}/delay?${qs}`; + return await fetch(fullUrl, init); +} + +export async function fetchProviderProxies(config) { + const { url, init } = getURLAndInit(config); + const res = await fetch(url + '/providers/proxies', init); + if (res.status === 404) { + return { providers: {} }; + } + return await res.json(); +} + +export async function updateProviderByName(config, name) { + const { url, init } = getURLAndInit(config); + const options = { ...init, method: 'PUT' }; + return await fetch(url + '/providers/proxies/' + encodeURIComponent(name), options); +} + +export async function healthcheckProviderByName(config, name) { + const { url, init } = getURLAndInit(config); + const options = { ...init, method: 'GET' }; + return await fetch( + url + '/providers/proxies/' + encodeURIComponent(name) + '/healthcheck', + options + ); +} diff --git a/src/api/rule-provider.ts b/src/api/rule-provider.ts new file mode 100644 index 0000000..14d9917 --- /dev/null +++ b/src/api/rule-provider.ts @@ -0,0 +1,84 @@ +import { getURLAndInit } from '~/misc/request-helper'; +import { ClashAPIConfig } from '~/types'; + +export type RuleProvider = RuleProviderAPIItem & { idx: number }; + +export type RuleProviderAPIItem = { + behavior: string; + name: string; + ruleCount: number; + type: 'Rule'; + // example value "2020-06-30T16:23:01.44143802+08:00" + updatedAt: string; + vehicleType: 'HTTP' | 'File'; +}; + +type RuleProviderAPIData = { + providers: Record; +}; + +function normalizeAPIResponse(data: RuleProviderAPIData) { + const providers = data.providers; + const names = Object.keys(providers); + const byName: Record = {}; + + // attach an idx to each item + for (let i = 0; i < names.length; i++) { + const name = names[i]; + byName[name] = { ...providers[name], idx: i }; + } + + return { byName, names }; +} + +export async function fetchRuleProviders(endpoint: string, apiConfig: ClashAPIConfig) { + const { url, init } = getURLAndInit(apiConfig); + + let data = { providers: {} }; + try { + const res = await fetch(url + endpoint, init); + if (res.ok) { + data = await res.json(); + } + } catch (err) { + // log and ignore + // eslint-disable-next-line no-console + console.log('failed to GET /providers/rules', err); + } + return normalizeAPIResponse(data); +} + +export async function refreshRuleProviderByName({ + name, + apiConfig, +}: { + name: string; + apiConfig: ClashAPIConfig; +}) { + const { url, init } = getURLAndInit(apiConfig); + try { + const res = await fetch(url + `/providers/rules/${name}`, { + method: 'PUT', + ...init, + }); + return res.ok; + } catch (err) { + // log and ignore + // eslint-disable-next-line no-console + console.log('failed to PUT /providers/rules/:name', err); + return false; + } +} + +export async function updateRuleProviders({ + names, + apiConfig, +}: { + names: string[]; + apiConfig: ClashAPIConfig; +}) { + for (let i = 0; i < names.length; i++) { + // run in sequence + await refreshRuleProviderByName({ name: names[i], apiConfig }); + } +} diff --git a/src/api/rules.ts b/src/api/rules.ts new file mode 100644 index 0000000..3edb8d4 --- /dev/null +++ b/src/api/rules.ts @@ -0,0 +1,40 @@ +import invariant from 'invariant'; + +import { getURLAndInit } from '~/misc/request-helper'; +import { ClashAPIConfig } from '~/types'; + +// const endpoint = '/rules'; + +type RuleItem = RuleAPIItem & { id: number }; + +type RuleAPIItem = { + type: string; + payload: string; + proxy: string; +}; + +function normalizeAPIResponse(json: { rules: Array }): Array { + invariant( + json.rules && json.rules.length >= 0, + 'there is no valid rules list in the rules API response' + ); + + // attach an id + return json.rules.map((r: RuleAPIItem, i: number) => ({ ...r, id: i })); +} + +export async function fetchRules(endpoint: string, apiConfig: ClashAPIConfig) { + let json = { rules: [] }; + try { + const { url, init } = getURLAndInit(apiConfig); + const res = await fetch(url + endpoint, init); + if (res.ok) { + json = await res.json(); + } + } catch (err) { + // log and ignore + // eslint-disable-next-line no-console + console.log('failed to fetch rules', err); + } + return normalizeAPIResponse(json); +} diff --git a/src/api/traffic.ts b/src/api/traffic.ts new file mode 100644 index 0000000..34e9c91 --- /dev/null +++ b/src/api/traffic.ts @@ -0,0 +1,119 @@ +import { ClashAPIConfig } from '~/types'; + +import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper'; + +const endpoint = '/traffic'; +const textDecoder = new TextDecoder('utf-8'); + +const Size = 150; + +const traffic = { + labels: Array(Size).fill(0), + up: Array(Size), + down: Array(Size), + + size: Size, + subscribers: [], + appendData(o: { up: number; down: number }) { + this.up.shift(); + this.down.shift(); + this.labels.shift(); + + const l = Date.now(); + this.up.push(o.up); + this.down.push(o.down); + this.labels.push(l); + + this.subscribers.forEach((f) => f(o)); + }, + + subscribe(listener: (x: any) => void) { + this.subscribers.push(listener); + return () => { + const idx = this.subscribers.indexOf(listener); + this.subscribers.splice(idx, 1); + }; + }, +}; + +let fetched = false; +let decoded = ''; + +function parseAndAppend(x: string) { + traffic.appendData(JSON.parse(x)); +} + +function pump(reader: ReadableStreamDefaultReader) { + return reader.read().then(({ done, value }) => { + const str = textDecoder.decode(value, { stream: !done }); + decoded += str; + + const splits = decoded.split('\n'); + + const lastSplit = splits[splits.length - 1]; + + for (let i = 0; i < splits.length - 1; i++) { + parseAndAppend(splits[i]); + } + + if (done) { + parseAndAppend(lastSplit); + decoded = ''; + + // eslint-disable-next-line no-console + console.log('GET /traffic streaming done'); + fetched = false; + return; + } else { + decoded = lastSplit; + } + return pump(reader); + }); +} + +// 1 OPEN +// other value CLOSED +// similar to ws readyState but not the same +// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState +let wsState: number; +function fetchData(apiConfig: ClashAPIConfig) { + if (fetched || wsState === 1) return traffic; + wsState = 1; + const url = buildWebSocketURL(apiConfig, endpoint); + const ws = new WebSocket(url); + ws.addEventListener('error', function (_ev) { + wsState = 3; + }); + ws.addEventListener('close', function (_ev) { + wsState = 3; + fetchDataWithFetch(apiConfig); + }); + ws.addEventListener('message', function (event) { + parseAndAppend(event.data); + }); + return traffic; +} + +function fetchDataWithFetch(apiConfig: ClashAPIConfig) { + if (fetched) return traffic; + fetched = true; + const { url, init } = getURLAndInit(apiConfig); + fetch(url + endpoint, init).then( + (response) => { + if (response.ok) { + const reader = response.body.getReader(); + pump(reader); + } else { + fetched = false; + } + }, + (err) => { + // eslint-disable-next-line no-console + console.log('fetch /traffic error', err); + fetched = false; + } + ); + return traffic; +} + +export { fetchData }; diff --git a/src/api/version.ts b/src/api/version.ts new file mode 100644 index 0000000..6c29125 --- /dev/null +++ b/src/api/version.ts @@ -0,0 +1,27 @@ +import { getURLAndInit } from '~/misc/request-helper'; +import { ClashAPIConfig } from '~/types'; + +type VersionData = { + version?: string; + premium?: boolean; + meta?: boolean; +}; + +export async function fetchVersion( + endpoint: string, + apiConfig: ClashAPIConfig +): Promise { + let json = {}; + try { + const { url, init } = getURLAndInit(apiConfig); + const res = await fetch(url + endpoint, init); + if (res.ok) { + json = await res.json(); + } + } catch (err) { + // log and ignore + // eslint-disable-next-line no-console + console.log(`failed to fetch ${endpoint}`, err); + } + return json; +} diff --git a/src/components/APIConfig.module.scss b/src/components/APIConfig.module.scss new file mode 100644 index 0000000..6581d46 --- /dev/null +++ b/src/components/APIConfig.module.scss @@ -0,0 +1,52 @@ +.root { + &:focus { + outline: none; + } +} + +.header { + display: flex; + justify-content: center; + align-items: center; + + .icon { + --stroke: #f3f3f3; + color: #20497e; + opacity: 0.7; + transition: opacity 400ms; + &:hover { + opacity: 1; + } + } +} + +.body { + padding: 15px 0 0; +} + +.hostnamePort { + display: flex; + + div { + flex: 1 1 auto; + } + + div:nth-child(2) { + flex-grow: 0; + flex-basis: 120px; + margin-left: 10px; + } +} + +.error { + height: 20px; + font-size: 0.8em; + color: #ff8b8b; +} + +.footer { + padding: 5px 0 10px; + display: flex; + justify-content: flex-end; + align-items: center; +} diff --git a/src/components/APIConfig.tsx b/src/components/APIConfig.tsx new file mode 100644 index 0000000..6de1f30 --- /dev/null +++ b/src/components/APIConfig.tsx @@ -0,0 +1,162 @@ +import * as React from 'react'; + +import { fetchConfigs } from '~/api/configs'; +import { BackendList } from '~/components/BackendList'; +import { addClashAPIConfig, getClashAPIConfig } from '~/store/app'; +import { State } from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +import s0 from './APIConfig.module.scss'; +import Button from './Button'; +import Field from './Field'; +import { connect } from './StateProvider'; +import SvgYacd from './SvgYacd'; + +const { useState, useRef, useCallback, useEffect } = React; +const Ok = 0; + +const mapState = (s: State) => ({ + apiConfig: getClashAPIConfig(s), +}); + +function APIConfig({ dispatch }) { + const [baseURL, setBaseURL] = useState(''); + const [secret, setSecret] = useState(''); + const [errMsg, setErrMsg] = useState(''); + + const userTouchedFlagRef = useRef(false); + const contentEl = useRef(null); + + const handleInputOnChange = useCallback((e) => { + userTouchedFlagRef.current = true; + setErrMsg(''); + const target = e.target; + const { name } = target; + const value = target.value; + switch (name) { + case 'baseURL': + setBaseURL(value); + break; + case 'secret': + setSecret(value); + break; + default: + throw new Error(`unknown input name ${name}`); + } + }, []); + + const onConfirm = useCallback(() => { + let unconfirmedBaseURL = baseURL; + if (unconfirmedBaseURL) { + const prefix = baseURL.substring(0, 7); + if (prefix.includes(':/')) { + // same logic in verify function + if (prefix !== 'http://' && prefix !== 'https:/') { + return [1, 'Must starts with http:// or https://']; + } + } else if (window.location.protocol) { + // only append scheme when prefix does not include scheme and current location includes scheme + unconfirmedBaseURL = `${window.location.protocol}//${unconfirmedBaseURL}`; + } + } + verify({ baseURL: unconfirmedBaseURL, secret }).then((ret) => { + if (ret[0] !== Ok) { + setErrMsg(ret[1]); + } else { + dispatch(addClashAPIConfig({ baseURL: unconfirmedBaseURL, secret })); + } + }); + }, [baseURL, secret, dispatch]); + + const handleContentOnKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if ( + e.target instanceof Element && + (!e.target.tagName || e.target.tagName.toUpperCase() !== 'INPUT') + ) { + return; + } + if (e.key !== 'Enter') return; + + onConfirm(); + }, + [onConfirm] + ); + + const detectApiServer = async () => { + // if there is already a clash API server at `/`, just use it as default value + const res = await fetch('/'); + res.json().then((data) => { + if (data['hello'] === 'clash') { + setBaseURL(window.location.origin); + } + }); + }; + useEffect(() => { + detectApiServer(); + }, []); + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
+
+
+ +
+
+
+
+ + +
+
+
{errMsg ? errMsg : null}
+
+
+
+ +
+ ); +} + +export default connect(mapState)(APIConfig); + +async function verify(apiConfig: ClashAPIConfig): Promise<[number, string?]> { + try { + new URL(apiConfig.baseURL); + } catch (e) { + if (apiConfig.baseURL) { + const prefix = apiConfig.baseURL.substring(0, 7); + if (prefix !== 'http://' && prefix !== 'https:/') { + return [1, 'Must starts with http:// or https://']; + } + } + + return [1, 'Invalid URL']; + } + try { + const res = await fetchConfigs(apiConfig); + if (res.status > 399) { + return [1, res.statusText]; + } + return [Ok]; + } catch (e) { + return [1, 'Failed to connect']; + } +} diff --git a/src/components/APIDiscovery.module.scss b/src/components/APIDiscovery.module.scss new file mode 100644 index 0000000..e2161f8 --- /dev/null +++ b/src/components/APIDiscovery.module.scss @@ -0,0 +1,33 @@ +.content.content { + background: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + + transform: none; + padding: 0; + border-radius: 0; + + display: flex; + justify-content: center; + overflow-y: auto; +} + +.container { + position: relative; + margin-left: 20px; + margin-right: 20px; +} + +.overlay.overlay { + background-color: var(--color-background); +} + +.fixed { + position: fixed; + padding: 16px; + bottom: 0; + right: 0; +} diff --git a/src/components/APIDiscovery.tsx b/src/components/APIDiscovery.tsx new file mode 100644 index 0000000..9340b60 --- /dev/null +++ b/src/components/APIDiscovery.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; + +import { ThemeSwitcher } from '~/components/shared/ThemeSwitcher'; +import { DOES_NOT_SUPPORT_FETCH, errors } from '~/misc/errors'; +import { getClashAPIConfig } from '~/store/app'; +import { fetchConfigs } from '~/store/configs'; +import { closeModal } from '~/store/modals'; +import { State } from '~/store/types'; + +import APIConfig from './APIConfig'; +import s0 from './APIDiscovery.module.scss'; +import Modal from './Modal'; +import { connect } from './StateProvider'; + +const { useCallback, useEffect } = React; + +function APIDiscovery({ dispatch, apiConfig, modals }) { + if (!window.fetch) { + const { detail } = errors[DOES_NOT_SUPPORT_FETCH]; + const err = new Error(detail); + // @ts-expect-error ts-migrate(2339) FIXME: Property 'code' does not exist on type 'Error'. + err.code = DOES_NOT_SUPPORT_FETCH; + throw err; + } + + const closeApiConfigModal = useCallback(() => { + dispatch(closeModal('apiConfig')); + }, [dispatch]); + useEffect(() => { + dispatch(fetchConfigs(apiConfig)); + }, [dispatch, apiConfig]); + + return ( + +
+ +
+ +
+ +
+
+ ); +} + +const mapState = (s: State) => ({ + modals: s.modals, + apiConfig: getClashAPIConfig(s), +}); + +export default connect(mapState)(APIDiscovery); diff --git a/src/components/BackendList.module.scss b/src/components/BackendList.module.scss new file mode 100644 index 0000000..6872d3a --- /dev/null +++ b/src/components/BackendList.module.scss @@ -0,0 +1,102 @@ +.ul { + position: relative; + margin: 0; + padding: 0; + list-style: none; + line-height: 1.8; + + --width-max-content: 230px; +} + +.li { + position: relative; + margin: 5px 0; + padding: 10px 0; + border-radius: 10px; + display: grid; + place-content: center; + grid-template-columns: 40px 1fr 40px; + grid-template-rows: 30px; + grid-template-areas: 'close url .'; + column-gap: 10px; + border: 1px solid var(--bg-near-transparent); +} + +.li:hover { + background-color: var(--bg-near-transparent); +} + +.close { + opacity: 0; + grid-area: close; + place-self: center; + cursor: pointer; +} + +.li:hover .close, +.li:hover .eye { + opacity: 1; +} +.close:focus, +.eye:focus { + opacity: 1; +} + +.hasSecret { + grid-template-rows: repeat(2, 30px); + grid-template-areas: + 'close url .' + 'close secret eye'; +} + +.url { + grid-area: url; +} +.secret { + grid-area: secret; +} +.eye { + grid-area: eye; + opacity: 0; + place-self: center; + cursor: pointer; +} + +.url, +.secret { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.btn { + outline: none; + appearance: none; + border: 1px solid transparent; + background-color: transparent; + color: inherit; + display: flex; + align-items: center; + padding: 5px; + border-radius: 100px; +} +.btn:focus { + border-color: var(--color-focus-blue); +} +.btn:hover:enabled { + background-color: var(--color-focus-blue); + color: white; +} +.btn:active:enabled { + transform: scale(0.97); +} +.btn:disabled { + color: var(--color-text-secondary); +} + +.url { + cursor: pointer; +} +.url:hover { + color: var(--color-text-highlight); +} diff --git a/src/components/BackendList.tsx b/src/components/BackendList.tsx new file mode 100644 index 0000000..d16a28e --- /dev/null +++ b/src/components/BackendList.tsx @@ -0,0 +1,142 @@ +import cx from 'clsx'; +import * as React from 'react'; +import { Eye, EyeOff, X as Close } from 'react-feather'; + +import { useToggle } from '~/hooks/basic'; +import { getClashAPIConfigs, getSelectedClashAPIConfigIndex } from '~/store/app'; +import { ClashAPIConfig } from '~/types'; + +import s from './BackendList.module.scss'; +import { connect, useStoreActions } from './StateProvider'; + +type Config = ClashAPIConfig & { addedAt: number }; + +const mapState = (s) => ({ + apiConfigs: getClashAPIConfigs(s), + selectedClashAPIConfigIndex: getSelectedClashAPIConfigIndex(s), +}); + +export const BackendList = connect(mapState)(BackendListImpl); + +function BackendListImpl({ + apiConfigs, + selectedClashAPIConfigIndex, +}: { + apiConfigs: Config[]; + selectedClashAPIConfigIndex: number; +}) { + const { + app: { removeClashAPIConfig, selectClashAPIConfig }, + } = useStoreActions(); + + const onRemove = React.useCallback( + (conf: ClashAPIConfig) => { + removeClashAPIConfig(conf); + }, + [removeClashAPIConfig] + ); + const onSelect = React.useCallback( + (conf: ClashAPIConfig) => { + selectClashAPIConfig(conf); + }, + [selectClashAPIConfig] + ); + + return ( + <> +
    + {apiConfigs.map((item, idx) => { + return ( +
  • + +
  • + ); + })} +
+ + ); +} + +function Item({ + baseURL, + secret, + disableRemove, + onRemove, + onSelect, +}: { + baseURL: string; + secret: string; + disableRemove: boolean; + onRemove: (x: ClashAPIConfig) => void; + onSelect: (x: ClashAPIConfig) => void; +}) { + const [show, toggle] = useToggle(); + const Icon = show ? EyeOff : Eye; + + const handleTap = React.useCallback((e: React.KeyboardEvent) => { + e.stopPropagation(); + }, []); + + return ( + <> + + onSelect({ baseURL, secret })} + onKeyUp={handleTap} + > + {baseURL} + + + {secret ? ( + <> + {show ? secret : '***'} + + {/* @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean | (() => void)' is not assignable to... Remove this comment to see the full error message */} + + + ) : null} + + ); +} + +function Button({ + children, + onClick, + className, + disabled, +}: { + children: React.ReactNode; + + onClick?: (e: React.MouseEvent) => unknown; + className: string; + disabled?: boolean; +}) { + return ( + + ); +} diff --git a/src/components/Button.module.scss b/src/components/Button.module.scss new file mode 100644 index 0000000..bdbbcb5 --- /dev/null +++ b/src/components/Button.module.scss @@ -0,0 +1,72 @@ +@import '~/styles/utils/custom-media'; + +.btn { + -webkit-appearance: none; + outline: none; + user-select: none; + position: relative; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--color-btn-fg); + background: var(--color-btn-bg); + border: 1px solid #555; + border-radius: 100px; + &:focus { + border-color: var(--color-focus-blue); + } + &:hover { + background: #387cec; + border: 1px solid #387cec; + color: #fff; + } + &:active { + transform: scale(0.97); + } + + font-size: 0.75em; + padding: 4px 7px; + @media (--breakpoint-not-small) { + font-size: small; + padding: 6px 12px; + } + + &.minimal { + border-color: transparent; + background: none; + &:focus { + border-color: var(--color-focus-blue); + } + &:hover { + color: #fff; + background: #387cec; + border: 1px solid #387cec; + } + } +} + +.btn:disabled { + opacity: 0.5; +} + +.btnInternal { + display: flex; + align-items: center; + justify-content: center; + column-gap: 4px; +} + +.btnStart { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.loadingContainer { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: inline-flex; +} diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 0000000..9bd0d61 --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,82 @@ +import cx from 'clsx'; +import * as React from 'react'; + +import s0 from './Button.module.scss'; +import { LoadingDot } from './shared/Basic'; + +const { forwardRef, useCallback } = React; + +type ButtonInternalProps = { + children?: React.ReactNode; + label?: string; + text?: string; + start?: React.ReactNode | (() => React.ReactNode); +}; + +type ButtonProps = { + isLoading?: boolean; + onClick?: (e: React.MouseEvent) => unknown; + disabled?: boolean; + kind?: 'primary' | 'minimal'; + className?: string; + title?: string; +} & ButtonInternalProps; + +function Button(props: ButtonProps, ref: React.Ref) { + const { + onClick, + disabled = false, + isLoading, + kind = 'primary', + className, + children, + label, + text, + start, + ...restProps + } = props; + const internalProps = { children, label, text, start }; + const internalOnClick = useCallback( + (e) => { + if (isLoading) return; + onClick && onClick(e); + }, + [isLoading, onClick] + ); + const btnClassName = cx(s0.btn, { [s0.minimal]: kind === 'minimal' }, className); + return ( + + ); +} + +function ButtonInternal({ children, label, text, start }: ButtonInternalProps) { + return ( +
+ {start && ( + {typeof start === 'function' ? start() : start} + )} + {children || label || text} +
+ ); +} + +export default forwardRef(Button); diff --git a/src/components/Collapsible.tsx b/src/components/Collapsible.tsx new file mode 100644 index 0000000..47b882c --- /dev/null +++ b/src/components/Collapsible.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import ResizeObserver from 'resize-observer-polyfill'; + +import { framerMotionResouce } from '../misc/motion'; + +const { memo, useState, useRef, useEffect } = React; + +function usePrevious(value) { + const ref = useRef(); + useEffect(() => void (ref.current = value), [value]); + return ref.current; +} + +function useMeasure() { + const ref = useRef(); + const [bounds, set] = useState({ height: 0 }); + useEffect(() => { + const ro = new ResizeObserver(([entry]) => set(entry.contentRect)); + if (ref.current) ro.observe(ref.current); + return () => ro.disconnect(); + }, []); + return [ref, bounds]; +} + +const variantsCollpapsibleWrap = { + initialOpen: { + height: 'auto', + transition: { duration: 0 }, + }, + open: (height) => ({ + height, + opacity: 1, + visibility: 'visible', + transition: { duration: 0.3 }, + }), + closed: { + height: 0, + opacity: 0, + visibility: 'hidden', + overflowY: 'hidden', + transition: { duration: 0.3 }, + }, +}; + +const variantsCollpapsibleChildContainer = { + open: {}, + closed: {}, +}; + +// @ts-expect-error ts-migrate(2339) FIXME: Property 'isOpen' does not exist on type '{ childr... Remove this comment to see the full error message +const Collapsible = memo(({ children, isOpen }) => { + const module = framerMotionResouce.read(); + const motion = module.motion; + const previous = usePrevious(isOpen); + // @ts-expect-error ts-migrate(2339) FIXME: Property 'height' does not exist on type 'MutableR... Remove this comment to see the full error message + const [refToMeature, { height }] = useMeasure(); + return ( +
+ + + {children} + + +
+ ); +}); + +export default Collapsible; diff --git a/src/components/CollapsibleSectionHeader.module.scss b/src/components/CollapsibleSectionHeader.module.scss new file mode 100644 index 0000000..b654f35 --- /dev/null +++ b/src/components/CollapsibleSectionHeader.module.scss @@ -0,0 +1,39 @@ +.header { + display: flex; + align-items: center; + + &:focus { + outline: none; + } + + .arrow { + display: inline-flex; + transform: rotate(0deg); + transition: transform 0.3s; + + &.isOpen { + transform: rotate(180deg); + } + + &:focus { + outline: var(--color-focus-blue) solid 1px; + } + } +} + +.btn { + margin-left: 5px; +} + +/* TODO duplicate with connQty in Connections.module.css */ +.qty { + font-family: var(--font-normal); + font-size: 0.75em; + margin-left: 3px; + padding: 2px 7px; + display: inline-flex; + justify-content: center; + align-items: center; + background-color: var(--bg-near-transparent); + border-radius: 30px; +} diff --git a/src/components/CollapsibleSectionHeader.tsx b/src/components/CollapsibleSectionHeader.tsx new file mode 100644 index 0000000..8b701e1 --- /dev/null +++ b/src/components/CollapsibleSectionHeader.tsx @@ -0,0 +1,49 @@ +import cx from 'clsx'; +import * as React from 'react'; +import { ChevronDown } from 'react-feather'; + +import Button from './Button'; +import s from './CollapsibleSectionHeader.module.scss'; +import { SectionNameType } from './shared/Basic'; + +type Props = { + name: string; + type: string; + qty?: number; + toggle?: () => void; + isOpen?: boolean; +}; + +export default function Header({ name, type, toggle, isOpen, qty }: Props) { + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + e.preventDefault(); + if (e.key === 'Enter' || e.key === ' ') { + toggle(); + } + }, + [toggle] + ); + return ( +
+
+ +
+ + {typeof qty === 'number' ? {qty} : null} + + +
+ ); +} diff --git a/src/components/Config.module.scss b/src/components/Config.module.scss new file mode 100644 index 0000000..b9998a3 --- /dev/null +++ b/src/components/Config.module.scss @@ -0,0 +1,43 @@ +@import '~/styles/utils/custom-media'; + +.root, +.section { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(49%, 1fr)); + max-width: 900px; + gap: 5px; + @media (--breakpoint-not-small) { + gap: 15px; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + } +} + +.root, +.section { + padding: 6px 15px 10px; + @media (--breakpoint-not-small) { + padding: 10px 40px 15px; + } +} + +.wrapSwitch { + height: 40px; + display: flex; + align-items: center; +} + +.sep { + max-width: 900px; + padding: 0 15px; + @media (--breakpoint-not-small) { + padding: 0 40px; + } + > div { + border-top: 1px dashed #373737; + } +} + +.label { + padding: 15px 0; + font-size: small; +} diff --git a/src/components/Config.tsx b/src/components/Config.tsx new file mode 100644 index 0000000..84be48e --- /dev/null +++ b/src/components/Config.tsx @@ -0,0 +1,403 @@ +import * as React from 'react'; +import { DownloadCloud, LogOut, RotateCw, Trash2 } from 'react-feather'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; + +import * as logsApi from '~/api/logs'; +import { fetchVersion } from '~/api/version'; +import Select from '~/components/shared/Select'; +import { ClashGeneralConfig, DispatchFn, State } from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +import { getClashAPIConfig, getLatencyTestUrl, getSelectedChartStyleIndex } from '../store/app'; +import { + fetchConfigs, + flushFakeIPPool, + getConfigs, + reloadConfigFile, + updateConfigs, + updateGeoDatabasesFile, +} from '../store/configs'; +import { openModal } from '../store/modals'; +import Button from './Button'; +import s0 from './Config.module.scss'; +import ContentHeader from './ContentHeader'; +import Input, { SelfControlledInput } from './Input'; +import { Selection2 } from './Selection'; +import { connect, useStoreActions } from './StateProvider'; +import Switch from './SwitchThemed'; +import TrafficChartSample from './TrafficChartSample'; + +const { useEffect, useState, useCallback, useRef } = React; + +const propsList = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]; + +const logLeveOptions = [ + ['debug', 'Debug'], + ['info', 'Info'], + ['warning', 'Warning'], + ['error', 'Error'], + ['silent', 'Silent'], +]; + +const portFields = [ + { key: 'port', label: 'Http Port' }, + { key: 'socks-port', label: 'Socks5 Port' }, + { key: 'mixed-port', label: 'Mixed Port' }, + { key: 'redir-port', label: 'Redir Port' }, + { key: 'mitm-port', label: 'MITM Port' }, +]; + +const langOptions = [ + ['zh', '中文'], + ['en', 'English'], +]; + +const modeOptions = [ + ['direct', 'Direct'], + ['rule', 'Rule'], + ['script', 'Script'], + ['global', 'Global'], +]; + +const tunStackOptions = [ + ['gvisor', 'gVisor'], + ['system', 'System'], + ['lwip', 'LWIP'], +]; + +const mapState = (s: State) => ({ + configs: getConfigs(s), + apiConfig: getClashAPIConfig(s), +}); + +const mapState2 = (s: State) => ({ + selectedChartStyleIndex: getSelectedChartStyleIndex(s), + latencyTestUrl: getLatencyTestUrl(s), + apiConfig: getClashAPIConfig(s), +}); + +const Config = connect(mapState2)(ConfigImpl); +export default connect(mapState)(ConfigContainer); + +function ConfigContainer({ dispatch, configs, apiConfig }) { + useEffect(() => { + dispatch(fetchConfigs(apiConfig)); + }, [dispatch, apiConfig]); + return ; +} + +type ConfigImplProps = { + dispatch: DispatchFn; + configs: ClashGeneralConfig; + selectedChartStyleIndex: number; + latencyTestUrl: string; + apiConfig: ClashAPIConfig; +}; + +function ConfigImpl({ + dispatch, + configs, + selectedChartStyleIndex, + latencyTestUrl, + apiConfig, +}: ConfigImplProps) { + const { t, i18n } = useTranslation(); + + const [configState, setConfigStateInternal] = useState(configs); + const refConfigs = useRef(configs); + useEffect(() => { + if (refConfigs.current !== configs) { + setConfigStateInternal(configs); + } + refConfigs.current = configs; + }, [configs]); + + const openAPIConfigModal = useCallback(() => { + dispatch(openModal('apiConfig')); + }, [dispatch]); + + const setConfigState = useCallback( + (name: string, val: any) => { + setConfigStateInternal({ ...configState, [name]: val }); + }, + [configState] + ); + + const setTunConfigState = useCallback( + (name: any, val: any) => { + const tun = { ...configState.tun, [name]: val }; + setConfigStateInternal({ ...configState, tun: { ...tun } }); + }, + [configState] + ); + + const handleInputOnChange = useCallback( + ({ name, value }) => { + switch (name) { + case 'mode': + case 'log-level': + case 'allow-lan': + case 'sniffing': + setConfigState(name, value); + dispatch(updateConfigs(apiConfig, { [name]: value })); + if (name === 'log-level') { + logsApi.reconnect({ ...apiConfig, logLevel: value }); + } + break; + case 'mitm-port': + case 'redir-port': + case 'socks-port': + case 'mixed-port': + case 'port': + if (value !== '') { + const num = parseInt(value, 10); + if (num < 0 || num > 65535) return; + } + setConfigState(name, value); + break; + case 'enable': + case 'stack': + setTunConfigState(name, value); + dispatch(updateConfigs(apiConfig, { tun: { [name]: value } })); + break; + default: + return; + } + }, + [apiConfig, dispatch, setConfigState, setTunConfigState] + ); + + const { selectChartStyleIndex, updateAppConfig } = useStoreActions(); + + const handleInputOnBlur = useCallback( + ( + e: + | React.FocusEvent + | React.ChangeEvent + ) => { + const { name, value } = e.target; + + switch (name) { + case 'port': + case 'socks-port': + case 'mixed-port': + case 'redir-port': + case 'mitm-port': { + const num = parseInt(value, 10); + if (num < 0 || num > 65535) return; + dispatch(updateConfigs(apiConfig, { [name]: num })); + break; + } + case 'latencyTestUrl': { + updateAppConfig(name, value); + break; + } + case 'device name': + case 'interface name': + break; + default: + throw new Error(`unknown input name ${name}`); + } + }, + [apiConfig, dispatch, updateAppConfig] + ); + const handleReloadConfigFile = useCallback(() => { + dispatch(reloadConfigFile(apiConfig)); + }, [apiConfig, dispatch]); + + const handleUpdateGeoDatabasesFile = useCallback(() => { + dispatch(updateGeoDatabasesFile(apiConfig)); + }, [apiConfig, dispatch]); + + const handleFlushFakeIPPool = useCallback(() => { + dispatch(flushFakeIPPool(apiConfig)); + }, [apiConfig, dispatch]); + + const { data: version } = useQuery(['/version', apiConfig], () => + fetchVersion('/version', apiConfig) + ); + + return ( +
+ +
+ {portFields.map((f) => + configState[f.key] !== undefined ? ( +
+
{f.label}
+ handleInputOnChange({ name, value })} + onBlur={handleInputOnBlur} + /> +
+ ) : null + )} + +
+
Mode
+ handleInputOnChange({ name: 'log-level', value: e.target.value })} + /> +
+ +
+
{t('allow_lan')}
+
+ + handleInputOnChange({ name: 'allow-lan', value: value }) + } + /> +
+
+ {version.meta && ( +
+
{t('tls_sniffing')}
+
+ + handleInputOnChange({ name: 'sniffing', value: value }) + } + /> +
+
+ )} +
+
+
+
+ {version.meta && ( + <> +
+
+
{t('enable_tun_device')}
+
+ + handleInputOnChange({ name: 'enable', value: value }) + } + /> +
+
+
+
TUN IP Stack
+ +
+
+
Interface Name
+ +
+
+
+
+
+
+
+
Reload
+
+
+
GEO Databases
+
+
+
FakeIP
+
+
+
+
+
+ + )} + +
+
+
{t('latency_test_url')}
+ +
+
+
{t('lang')}
+
+ setFilterKeyword(e.target.value)} + /> +
+
+
+
+ + <>{renderTableOrPlaceholder(filteredConns)} + : } + mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} + style={fabPosition} + text={isRefreshPaused ? t('Resume Refresh') : t('Pause Refresh')} + onClick={toggleIsRefreshPaused} + > + + + + + + {renderTableOrPlaceholder(filteredClosedConns)} +
+
+ + +
+ ); +} + +const mapState = (s: State) => ({ + apiConfig: getClashAPIConfig(s), +}); + +export default connect(mapState)(Conn); diff --git a/src/components/ContentHeader.module.scss b/src/components/ContentHeader.module.scss new file mode 100644 index 0000000..3fc4cc7 --- /dev/null +++ b/src/components/ContentHeader.module.scss @@ -0,0 +1,18 @@ +@import '~/styles/utils/custom-media'; + +.root { + height: 76px; + display: flex; + align-items: center; +} + +.h1 { + padding: 0 15px; + font-size: 1.7em; + @media (--breakpoint-not-small) { + padding: 0 40px; + font-size: 2em; + } + text-align: left; + margin: 0; +} diff --git a/src/components/ContentHeader.tsx b/src/components/ContentHeader.tsx new file mode 100644 index 0000000..473cd4c --- /dev/null +++ b/src/components/ContentHeader.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import s0 from './ContentHeader.module.scss'; + +type Props = { + title: string; +}; + +function ContentHeader({ title }: Props) { + return ( +
+

{title}

+
+ ); +} + +export default React.memo(ContentHeader); diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..f95c581 --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; + +// import { getSentry } from '../misc/sentry'; +import { deriveMessageFromError, Err } from '../misc/errors'; +import ErrorBoundaryFallback from './ErrorBoundaryFallback'; + +type Props = { + children: React.ReactNode; +}; + +type State = { + error?: Err; +}; + +class ErrorBoundary extends React.Component { + state = { error: null }; + + static getDerivedStateFromError(error: Err) { + return { error }; + } + + render() { + if (this.state.error) { + const { message, detail } = deriveMessageFromError(this.state.error); + return ; + } else { + return this.props.children; + } + } +} + +export default ErrorBoundary; diff --git a/src/components/ErrorBoundaryFallback.module.scss b/src/components/ErrorBoundaryFallback.module.scss new file mode 100644 index 0000000..6133568 --- /dev/null +++ b/src/components/ErrorBoundaryFallback.module.scss @@ -0,0 +1,37 @@ +.root { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow: hidden; + + padding: 20px; + background: var(--color-background); + color: var(--color-text); + text-align: center; +} + +.yacd { + color: #2a477a; + opacity: 0.6; + display: flex; + justify-content: center; + align-items: center; + padding: 40px; +} + +.link { + display: inline-flex; + align-items: center; + + color: var(--color-text-secondary); + &:hover, + &:active { + color: #387cec; + } + + svg { + margin-right: 5px; + } +} diff --git a/src/components/ErrorBoundaryFallback.tsx b/src/components/ErrorBoundaryFallback.tsx new file mode 100644 index 0000000..9f41d91 --- /dev/null +++ b/src/components/ErrorBoundaryFallback.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import s0 from './ErrorBoundaryFallback.module.scss'; +import SvgGithub from './SvgGithub'; +import SvgYacd from './SvgYacd'; +const yacdRepoIssueUrl = 'https://github.com/metacubex/yacd'; + +type Props = { + message?: string; + detail?: string; +}; + +function ErrorBoundaryFallback({ message, detail }: Props) { + return ( +
+
+ +
+ {message ?

{message}

: null} + {detail ?

{detail}

: null} +

+ + + metacubex/yacd + +

+
+ ); +} + +export default ErrorBoundaryFallback; diff --git a/src/components/Field.module.scss b/src/components/Field.module.scss new file mode 100644 index 0000000..72a5149 --- /dev/null +++ b/src/components/Field.module.scss @@ -0,0 +1,42 @@ +.root { + position: relative; + padding: 10px 0; + input { + -webkit-appearance: none; + background-color: transparent; + background-image: none; + border: none; + border-radius: 0; + border-bottom: 1px solid var(--color-input-border); + box-sizing: border-box; + color: inherit; + display: inline-block; + font-size: inherit; + height: 40px; + outline: none; + padding: 0 4px; + width: 100%; + &:focus { + border-color: var(--color-focus-blue); + } + } + + label { + position: absolute; + left: 5px; + bottom: 22px; + transition: transform 150ms ease-in-out; + transform-origin: 0 0; + font-size: 0.9em; + &.floatAbove { + transform: scale(0.75) translateY(-25px); + } + } + + input { + &:focus + label { + color: var(--color-focus-blue); + transform: scale(0.75) translateY(-25px); + } + } +} diff --git a/src/components/Field.tsx b/src/components/Field.tsx new file mode 100644 index 0000000..a0d43cf --- /dev/null +++ b/src/components/Field.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +import s from './Field.module.scss'; + +const { useCallback } = React; + +type Props = { + name: string; + value?: string | number; + type?: 'text' | 'number'; + onChange?: (...args: any[]) => any; + id?: string; + label?: string; + placeholder?: string; +}; + +export default function Field({ id, label, value, onChange, ...props }: Props) { + const valueOnChange = useCallback((e) => onChange(e), [onChange]); + return ( +
+ + +
+ ); +} diff --git a/src/components/Home.module.scss b/src/components/Home.module.scss new file mode 100644 index 0000000..cd1fe30 --- /dev/null +++ b/src/components/Home.module.scss @@ -0,0 +1,8 @@ +@import '~/styles/utils/custom-media'; + +.root { + padding: 6px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} diff --git a/src/components/Home.tsx b/src/components/Home.tsx new file mode 100644 index 0000000..d7ddbab --- /dev/null +++ b/src/components/Home.tsx @@ -0,0 +1,27 @@ +import React, { Suspense } from 'react'; +import { useTranslation } from 'react-i18next'; + +import ContentHeader from './ContentHeader'; +import s0 from './Home.module.scss'; +import Loading from './Loading'; +import TrafficChart from './TrafficChart'; +import TrafficNow from './TrafficNow'; + +export default function Home() { + const { t } = useTranslation(); + return ( +
+ +
+
+ +
+
+ }> + + +
+
+
+ ); +} diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx new file mode 100644 index 0000000..560fb6d --- /dev/null +++ b/src/components/Icon.tsx @@ -0,0 +1,20 @@ +import cx from 'clsx'; +import React from 'react'; + +type Props = { + id: string; + width?: number; + height?: number; + className?: string; +}; + +const Icon = ({ id, width = 20, height = 20, className, ...props }: Props) => { + const c = cx('icon', id, className); + const href = '#' + id; + return ( + + + + ); +}; +export default React.memo(Icon); diff --git a/src/components/Input.module.scss b/src/components/Input.module.scss new file mode 100644 index 0000000..021fe18 --- /dev/null +++ b/src/components/Input.module.scss @@ -0,0 +1,26 @@ +.input { + -webkit-appearance: none; + background-color: var(--color-input-bg); + background-image: none; + border-radius: 4px; + border: 1px solid var(--color-input-border); + box-sizing: border-box; + color: inherit; + display: inline-block; + font-size: inherit; + height: 35px; + outline: none; + padding: 0 15px; + width: 100%; + font-size: small; +} + +.input:focus { + box-shadow: rgba(66, 153, 225, 0.6) 0px 0px 0px 3px; +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} diff --git a/src/components/Input.tsx b/src/components/Input.tsx new file mode 100644 index 0000000..ade19b2 --- /dev/null +++ b/src/components/Input.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +import s0 from './Input.module.scss'; + +const { useState, useRef, useEffect, useCallback } = React; + +export default function Input(props: React.InputHTMLAttributes) { + return ; +} + +export function SelfControlledInput({ value, ...restProps }) { + const [internalValue, setInternalValue] = useState(value); + const refValue = useRef(value); + useEffect(() => { + if (refValue.current !== value) { + // ideally we should only do this when this input is not focused + setInternalValue(value); + } + refValue.current = value; + }, [value]); + const onChange = useCallback((e) => setInternalValue(e.target.value), [setInternalValue]); + + return ; +} diff --git a/src/components/Loading.module.scss b/src/components/Loading.module.scss new file mode 100644 index 0000000..c3f1d16 --- /dev/null +++ b/src/components/Loading.module.scss @@ -0,0 +1,28 @@ +.loading { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.spinner { + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + animation: rotate 1s steps(12, end) infinite; + background: transparent + url('data:image/svg+xml;charset=utf8, %3Csvg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 100 100"%3E%3Cpath fill="none" d="M0 0h100v100H0z"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23E9E9E9" rx="5" ry="5" transform="translate(0 -30)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23989697" rx="5" ry="5" transform="rotate(30 105.98 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%239B999A" rx="5" ry="5" transform="rotate(60 75.98 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23A3A1A2" rx="5" ry="5" transform="rotate(90 65 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23ABA9AA" rx="5" ry="5" transform="rotate(120 58.66 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23B2B2B2" rx="5" ry="5" transform="rotate(150 54.02 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23BAB8B9" rx="5" ry="5" transform="rotate(180 50 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23C2C0C1" rx="5" ry="5" transform="rotate(-150 45.98 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23CBCBCB" rx="5" ry="5" transform="rotate(-120 41.34 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23D2D2D2" rx="5" ry="5" transform="rotate(-90 35 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23DADADA" rx="5" ry="5" transform="rotate(-60 24.02 65)"/%3E%3Crect width="7" height="20" x="46.5" y="40" fill="%23E2E2E2" rx="5" ry="5" transform="rotate(-30 -5.98 65)"/%3E%3C/svg%3E') + no-repeat; + background-size: 100%; +} + +@keyframes rotate { + 0% { + transform: rotate3d(0, 0, 1, 0deg); + } + 100% { + transform: rotate3d(0, 0, 1, 360deg); + } +} diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx new file mode 100644 index 0000000..12ced75 --- /dev/null +++ b/src/components/Loading.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import s from './Loading.module.scss'; + +type Props = { + height?: string; +}; + +const Loading = ({ height }: Props) => { + const style = height ? { height } : {}; + return ( +
+
+
+ ); +}; + +export default Loading; diff --git a/src/components/Loading2.module.scss b/src/components/Loading2.module.scss new file mode 100644 index 0000000..067281e --- /dev/null +++ b/src/components/Loading2.module.scss @@ -0,0 +1,8 @@ +.lo { + opacity: 0.5; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/components/Loading2.tsx b/src/components/Loading2.tsx new file mode 100644 index 0000000..b847eb4 --- /dev/null +++ b/src/components/Loading2.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +import s0 from './Loading2.module.scss'; +import SvgYacd from './SvgYacd'; + +function Loading() { + return ( +
+ +
+ ); +} + +export default Loading; diff --git a/src/components/LogSearch.ts b/src/components/LogSearch.ts new file mode 100644 index 0000000..9c7879f --- /dev/null +++ b/src/components/LogSearch.ts @@ -0,0 +1,6 @@ +import { getSearchText, updateSearchText } from '../store/logs'; +import Search from './Search'; +import { connect } from './StateProvider'; + +const mapState = (s) => ({ searchText: getSearchText(s), updateSearchText }); +export default connect(mapState)(Search); diff --git a/src/components/Logs.module.scss b/src/components/Logs.module.scss new file mode 100644 index 0000000..2eb0022 --- /dev/null +++ b/src/components/Logs.module.scss @@ -0,0 +1,75 @@ +.logMeta { + font-size: 0.8em; + margin-bottom: 5px; + display: block; + line-height: 1.55em; +} + +.logType { + flex-shrink: 0; + text-align: center; + width: 66px; + border-radius: 100px; + padding: 3px 5px; + margin: 0 8px; +} + +.logTime { + flex-shrink: 0; + color: #fb923c; +} + +.logText { + flex-shrink: 0; + color: #888; + align-items: center; + line-height: 1.35em; + /* force wrap */ + width: 100%; + @media (max-width: 768px) { + display: inline-block; + } +} + +/*******************/ + +.logsWrapper { + margin: 45px; + padding: 10px; + background-color: var(--bg-log-info-card); + border-radius: 4px; + color: var(--color-text); + overflow-y: auto; + @media (max-width: 768px) { + margin: 25px; + } + :global { + .log { + margin-bottom: 10px; + //background: var(--color-background); + } + .log.even { + //background: var(--color-background); + } + } +} + +/*******************/ + +.logPlaceholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #2d2d30; + + div:nth-child(2) { + color: var(--color-text-secondary); + font-size: 1.4em; + opacity: 0.6; + } +} + +.logPlaceholderIcon { + opacity: 0.3; +} diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx new file mode 100644 index 0000000..e672eed --- /dev/null +++ b/src/components/Logs.tsx @@ -0,0 +1,106 @@ +import * as React from 'react'; +import { Pause, Play } from 'react-feather'; +import { useTranslation } from 'react-i18next'; + +import { fetchLogs, reconnect as reconnectLogs, stop as stopLogs } from '~/api/logs'; +import ContentHeader from '~/components/ContentHeader'; +import LogSearch from '~/components/LogSearch'; +import { connect, useStoreActions } from '~/components/StateProvider'; +import SvgYacd from '~/components/SvgYacd'; +import useRemainingViewPortHeight from '~/hooks/useRemainingViewPortHeight'; +import { getClashAPIConfig, getLogStreamingPaused } from '~/store/app'; +import { getLogLevel } from '~/store/configs'; +import { appendLog, getLogsForDisplay } from '~/store/logs'; +import { Log, State } from '~/store/types'; + +import s from './Logs.module.scss'; +import { Fab, position as fabPosition } from './shared/Fab'; + +const { useCallback, useEffect } = React; + +const paddingBottom = 30; +const colors = { + debug: '#389d3d', + info: '#58c3f2', + warning: '#cc5abb', + error: '#c11c1c', +}; + +const logTypes = { + debug: 'debug', + info: 'info', + warning: 'warn', + error: 'error', +}; + +type LogLineProps = Partial; + +function LogLine({ time, payload, type }: LogLineProps) { + return ( +
+ {time} + + [ {logTypes[type]} ] + + {payload} +
+ ); +} + +function Logs({ dispatch, logLevel, apiConfig, logs, logStreamingPaused }) { + const actions = useStoreActions(); + const toggleIsRefreshPaused = useCallback(() => { + logStreamingPaused ? reconnectLogs({ ...apiConfig, logLevel }) : stopLogs(); + // being lazy here + // ideally we should check the result of previous operation before updating this + actions.app.updateAppConfig('logStreamingPaused', !logStreamingPaused); + }, [apiConfig, logLevel, logStreamingPaused, actions.app]); + const appendLogInternal = useCallback((log) => dispatch(appendLog(log)), [dispatch]); + useEffect(() => { + fetchLogs({ ...apiConfig, logLevel }, appendLogInternal); + }, [apiConfig, logLevel, appendLogInternal]); + const [refLogsContainer, containerHeight] = useRemainingViewPortHeight(); + const { t } = useTranslation(); + + return ( +
+ + +
+ {logs.length === 0 ? ( +
+
+ +
+
{t('no_logs')}
+
+ ) : ( +
+ {logs.map((log, index) => ( +
+ +
+ ))} + + : } + mainButtonStyles={logStreamingPaused ? { background: '#e74c3c' } : {}} + style={fabPosition} + text={logStreamingPaused ? t('Resume Refresh') : t('Pause Refresh')} + onClick={toggleIsRefreshPaused} + > +
+ )} +
+
+ ); +} + +const mapState = (s: State) => ({ + logs: getLogsForDisplay(s), + logLevel: getLogLevel(s), + apiConfig: getClashAPIConfig(s), + logStreamingPaused: getLogStreamingPaused(s), +}); + +export default connect(mapState)(Logs); diff --git a/src/components/Modal.module.scss b/src/components/Modal.module.scss new file mode 100644 index 0000000..8f9807c --- /dev/null +++ b/src/components/Modal.module.scss @@ -0,0 +1,21 @@ +.overlay { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + background: #444; + z-index: 1024; +} + +.content { + outline: none; + position: relative; + color: var(--color-text); + background: #444; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 20px; + border-radius: 10px; +} diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx new file mode 100644 index 0000000..e91523c --- /dev/null +++ b/src/components/Modal.tsx @@ -0,0 +1,38 @@ +import cx from 'clsx'; +import * as React from 'react'; +import Modal, { Props as ReactModalProps } from 'react-modal'; + +import s0 from './Modal.module.scss'; + +type Props = ReactModalProps & { + isOpen: boolean; + onRequestClose: (...args: any[]) => any; + children: React.ReactNode; + className?: string; + overlayClassName?: string; +}; + +function ModalAPIConfig({ + isOpen, + onRequestClose, + className, + overlayClassName, + children, + ...otherProps +}: Props) { + const contentCls = cx(className, s0.content); + const overlayCls = cx(overlayClassName, s0.overlay); + return ( + + {children} + + ); +} + +export default React.memo(ModalAPIConfig); diff --git a/src/components/ModalCloseAllConnections.module.scss b/src/components/ModalCloseAllConnections.module.scss new file mode 100644 index 0000000..9bb7c6a --- /dev/null +++ b/src/components/ModalCloseAllConnections.module.scss @@ -0,0 +1,23 @@ +.overlay { + background-color: rgba(0, 0, 0, 0.6); +} +.cnt { + background-color: var(--bg-modal); + color: var(--color-text); + max-width: 300px; + line-height: 1.4; + transform: translate(-50%, -50%) scale(1.2); + opacity: 0.6; + transition: all 0.3s ease; +} +.afterOpen { + opacity: 1; + transform: translate(-50%, -50%) scale(1); +} + +.btngrp { + display: flex; + align-items: center; + justify-content: center; + margin-top: 30px; +} diff --git a/src/components/ModalCloseAllConnections.tsx b/src/components/ModalCloseAllConnections.tsx new file mode 100644 index 0000000..a2c4303 --- /dev/null +++ b/src/components/ModalCloseAllConnections.tsx @@ -0,0 +1,43 @@ +import cx from 'clsx'; +import React from 'react'; +import Modal from 'react-modal'; + +import Button from './Button'; +import modalStyle from './Modal.module.scss'; +import s from './ModalCloseAllConnections.module.scss'; + +const { useRef, useCallback, useMemo } = React; + +export default function Comp({ isOpen, onRequestClose, primaryButtonOnTap }) { + const primaryButtonRef = useRef(null); + const onAfterOpen = useCallback(() => { + primaryButtonRef.current.focus(); + }, []); + const className = useMemo( + () => ({ + base: cx(modalStyle.content, s.cnt), + afterOpen: s.afterOpen, + beforeClose: '', + }), + [] + ); + return ( + +

Are you sure you want to close all connections?

+
+ + {/* im lazy :) */} +
+ +
+ + ); +} diff --git a/src/components/Rule.module.scss b/src/components/Rule.module.scss new file mode 100644 index 0000000..e4652b0 --- /dev/null +++ b/src/components/Rule.module.scss @@ -0,0 +1,38 @@ +@import '~/styles/utils/custom-media'; + +.rule { + display: flex; + align-items: center; + padding: 6px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} + +.left { + width: 40px; + padding-right: 15px; + color: var(--color-text-secondary); + opacity: 0.4; +} + +.a { + display: flex; + align-items: center; + font-size: 12px; + opacity: 0.8; +} + +.b { + padding: 10px 0; + font-family: 'Roboto Mono', Menlo, monospace; + font-size: 12px; + @media (--breakpoint-not-small) { + font-size: 12px; + } +} + +.type { + width: 110px; + color: #3b5f76; +} diff --git a/src/components/Rule.tsx b/src/components/Rule.tsx new file mode 100644 index 0000000..b8ff70f --- /dev/null +++ b/src/components/Rule.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +import s0 from './Rule.module.scss'; + +const colorMap = { + _default: '#59caf9', + DIRECT: '#f5bc41', + REJECT: '#cb3166', +}; + +function getStyleFor({ proxy }) { + let color = colorMap._default; + if (colorMap[proxy]) { + color = colorMap[proxy]; + } + return { color }; +} + +type Props = { + id?: number; + type?: string; + payload?: string; + proxy?: string; +}; + +function Rule({ type, payload, proxy, id }: Props) { + const styleProxy = getStyleFor({ proxy }); + return ( +
+
{id}
+
+
{payload}
+
+
{type}
+
{proxy}
+
+
+
+ ); +} + +export default Rule; diff --git a/src/components/Rules.module.scss b/src/components/Rules.module.scss new file mode 100644 index 0000000..9c4e017 --- /dev/null +++ b/src/components/Rules.module.scss @@ -0,0 +1,23 @@ +@import '~/styles/utils/custom-media'; + +.header { + display: grid; + grid-template-columns: 1fr minmax(auto, 330px); + align-items: center; + + /* + * the content header has some padding + * we need to apply some right padding to this container then + */ + padding-right: 15px; + @media (--breakpoint-not-small) { + padding-right: 40px; + } +} + +.RuleProviderItemWrapper { + padding: 6px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} diff --git a/src/components/Rules.tsx b/src/components/Rules.tsx new file mode 100644 index 0000000..2d72515 --- /dev/null +++ b/src/components/Rules.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { areEqual, VariableSizeList } from 'react-window'; + +import { RuleProviderItem } from '~/components/rules/RuleProviderItem'; +import { useRuleAndProvider } from '~/components/rules/rules.hooks'; +import { RulesPageFab } from '~/components/rules/RulesPageFab'; +import { TextFilter } from '~/components/shared/TextFitler'; +import { ruleFilterText } from '~/store/rules'; +import { State } from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight'; +import { getClashAPIConfig } from '../store/app'; +import ContentHeader from './ContentHeader'; +import Rule from './Rule'; +import s from './Rules.module.scss'; +import { connect } from './StateProvider'; + +const { memo } = React; + +const paddingBottom = 30; + +type ItemData = { + rules: any[]; + provider: any; + apiConfig: ClashAPIConfig; +}; + +function itemKey(index: number, { rules, provider }: ItemData) { + const providerQty = provider.names.length; + + if (index < providerQty) { + return provider.names[index]; + } + const item = rules[index - providerQty]; + return item.id; +} + +function getItemSizeFactory({ provider }) { + return function getItemSize(idx: number) { + const providerQty = provider.names.length; + if (idx < providerQty) { + // provider + return 90; + } + // rule + return 60; + }; +} + +// @ts-expect-error ts-migrate(2339) FIXME: Property 'index' does not exist on type '{ childre... Remove this comment to see the full error message +const Row = memo(({ index, style, data }) => { + const { rules, provider, apiConfig } = data; + const providerQty = provider.names.length; + + if (index < providerQty) { + const name = provider.names[index]; + const item = provider.byName[name]; + return ( +
+ +
+ ); + } + + const r = rules[index - providerQty]; + return ( +
+ +
+ ); +}, areEqual); + +const mapState = (s: State) => ({ + apiConfig: getClashAPIConfig(s), +}); + +export default connect(mapState)(Rules); + +type RulesProps = { + apiConfig: ClashAPIConfig; +}; + +function Rules({ apiConfig }: RulesProps) { + const [refRulesContainer, containerHeight] = useRemainingViewPortHeight(); + const { rules, provider } = useRuleAndProvider(apiConfig); + const getItemSize = getItemSizeFactory({ provider }); + + const { t } = useTranslation(); + + return ( +
+
+ + +
+
+ + {Row} + +
+ {provider && provider.names && provider.names.length > 0 ? ( + + ) : null} +
+ ); +} diff --git a/src/components/Search.module.scss b/src/components/Search.module.scss new file mode 100644 index 0000000..58c20aa --- /dev/null +++ b/src/components/Search.module.scss @@ -0,0 +1,47 @@ +.RuleSearch { + padding: 0 40px 5px; + @media (max-width: 768px) { + padding: 0 25px 5px; + } +} + +.RuleSearchContainer { + position: relative; + height: 40px; + @media (max-width: 768px) { + height: 30px; + } +} + +.inputWrapper { + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 0; + width: 100%; +} + +.input { + -webkit-appearance: none; + background-color: var(--color-input-bg); + background-image: none; + border-radius: 20px; + border: 1px solid var(--color-input-border); + box-sizing: border-box; + color: #c1c1c1; + display: inline-block; + font-size: inherit; + height: 40px; + outline: none; + padding: 0 15px 0 35px; + transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); + width: 100%; +} + +.iconWrapper { + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 10px; + line-height: 0; +} diff --git a/src/components/Search.tsx b/src/components/Search.tsx new file mode 100644 index 0000000..0e0269c --- /dev/null +++ b/src/components/Search.tsx @@ -0,0 +1,46 @@ +import debounce from 'lodash-es/debounce'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Search as SearchIcon } from 'react-feather'; +import { useTranslation } from 'react-i18next'; + +import s0 from './Search.module.scss'; + +function RuleSearch({ dispatch, searchText, updateSearchText }) { + const { t } = useTranslation(); + const [text, setText] = useState(searchText); + const updateSearchTextInternal = useCallback( + (v) => { + dispatch(updateSearchText(v)); + }, + [dispatch, updateSearchText] + ); + const updateSearchTextDebounced = useMemo( + () => debounce(updateSearchTextInternal, 300), + [updateSearchTextInternal] + ); + const onChange = (e) => { + setText(e.target.value); + updateSearchTextDebounced(e.target.value); + }; + + return ( +
+
+
+ +
+
+ +
+
+
+ ); +} + +export default RuleSearch; diff --git a/src/components/Selection.module.scss b/src/components/Selection.module.scss new file mode 100644 index 0000000..5fa4576 --- /dev/null +++ b/src/components/Selection.module.scss @@ -0,0 +1,23 @@ +.fieldset { + margin: 0; + padding: 0; + border: 0; + display: flex; + flex-wrap: wrap; + flex-direction: row; +} + +.input + .cnt { + border: 1px solid transparent; + border-radius: 4px; + cursor: pointer; + margin-bottom: 5px; +} + +.input:focus + .cnt { + border-color: #387cec; +} + +.input:checked + .cnt { + border-color: #387cec; +} diff --git a/src/components/Selection.tsx b/src/components/Selection.tsx new file mode 100644 index 0000000..1b1f50e --- /dev/null +++ b/src/components/Selection.tsx @@ -0,0 +1,45 @@ +import cx from 'clsx'; +import React from 'react'; + +import s from './Selection.module.scss'; + +type SelectionProps = { + OptionComponent?: (...args: any[]) => any; + optionPropsList?: any[]; + selectedIndex?: number; + onChange?: (...args: any[]) => any; +}; + +export function Selection2({ + OptionComponent, + optionPropsList, + selectedIndex, + onChange, +}: SelectionProps) { + const inputCx = cx('visually-hidden', s.input); + const onInputChange = (e: React.ChangeEvent) => { + onChange(e.target.value); + }; + return ( +
+ {optionPropsList.map((props, idx) => { + return ( + + ); + })} +
+ ); +} diff --git a/src/components/SideBar.module.scss b/src/components/SideBar.module.scss new file mode 100644 index 0000000..ade5bf5 --- /dev/null +++ b/src/components/SideBar.module.scss @@ -0,0 +1,112 @@ +@import '~/styles/utils/custom-media'; + +.root { + background: var(--color-bg-sidebar); + min-width: 150px; + position: relative; +} + +.logoPlaceholder { + margin-top: 12px; + height: 120px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3FpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo1OGE5MDMzMS0zZWM3LTRhNGItOTUyNS1mZDNlYTZmZDU5ZGUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTFENDRDNEQ1OEQxMTFFQ0JBRjI4MzI2MEM4NEU4NDkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTFENDRDNEM1OEQxMTFFQ0JBRjI4MzI2MEM4NEU4NDkiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE1NWRkNmQzLTczOTItMmM0ZC1iNzQxLWYzY2JhYWQ5NjUxOCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1OGE5MDMzMS0zZWM3LTRhNGItOTUyNS1mZDNlYTZmZDU5ZGUiLz4gPC9y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'); + background-size: 80px; + background-repeat: no-repeat; + background-position: center; + @media (max-width: 768px) { + display: none; + } +} + +.rows { + @media (max-width: 768px) { + display: flex; + justify-content: space-between; + overflow: auto; + } +} + +/* a router link */ +.row { + color: var(--color-text); + text-decoration: none; + + display: flex; + align-items: center; + padding: 6px 10px; + @media (--breakpoint-not-small) { + padding: 8px 20px; + } + + @media (max-width: 768px) { + flex-direction: column; + } + + svg { + color: var(--color-icon); + width: 22px; + height: 22px; + + @media (--breakpoint-not-small) { + width: 24px; + height: 24px; + } + } +} + +.rowActive { + background: var(--color-sb-active-row-bg); + + @media (max-width: 768px) { + background: none; + border-bottom: 2px solid #387cec; + } +} + +.label { + padding-left: 14px; + font-size: 0.75em; + @media (max-width: 768px) { + padding-left: 0; + padding-top: 5px; + } + + @media (--breakpoint-not-small) { + font-size: 1em; + } +} + +.footer { + position: absolute; + bottom: 10px; + left: 50%; + transform: translateX(-50%); +} + +@media (max-width: 768px) { + .footer { + display: none; + } +} + +.iconWrapper { + --sz: 40px; + + width: var(--sz); + height: var(--sz); + display: flex; + justify-content: center; + align-items: center; + + outline: none; + padding: 5px; + color: var(--color-text); + border-radius: 100%; + border: 1px solid transparent; +} +.iconWrapper:hover { + opacity: 0.6; +} +.iconWrapper:focus { + border-color: var(--color-focus-blue); +} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx new file mode 100644 index 0000000..6ebd21e --- /dev/null +++ b/src/components/SideBar.tsx @@ -0,0 +1,105 @@ +import { Tooltip } from '@reach/tooltip'; +import cx from 'clsx'; +import * as React from 'react'; +import { Info } from 'react-feather'; +import { useTranslation } from 'react-i18next'; +import { FcAreaChart, FcDocument, FcGlobe, FcLink, FcRuler, FcSettings } from 'react-icons/fc'; +import { Link, useLocation } from 'react-router-dom'; + +import { ThemeSwitcher } from '~/components/shared/ThemeSwitcher'; + +import s from './SideBar.module.scss'; + +const icons = { + activity: FcAreaChart, + globe: FcGlobe, + command: FcRuler, + file: FcDocument, + settings: FcSettings, + link: FcLink, +}; + +const SideBarRow = React.memo(function SideBarRow({ + isActive, + to, + iconId, + labelText, +}: SideBarRowProps) { + const Comp = icons[iconId]; + const className = cx(s.row, isActive ? s.rowActive : null); + return ( + + +
{labelText}
+ + ); +}); + +interface SideBarRowProps { + isActive: boolean; + to: string; + iconId?: string; + labelText?: string; +} + +const pages = [ + { + to: '/', + iconId: 'activity', + labelText: 'Overview', + }, + { + to: '/proxies', + iconId: 'globe', + labelText: 'Proxies', + }, + { + to: '/rules', + iconId: 'command', + labelText: 'Rules', + }, + { + to: '/connections', + iconId: 'link', + labelText: 'Conns', + }, + { + to: '/configs', + iconId: 'settings', + labelText: 'Config', + }, + { + to: '/logs', + iconId: 'file', + labelText: 'Logs', + }, +]; + +export default function SideBar() { + const { t } = useTranslation(); + const location = useLocation(); + return ( +
+
+
+ {pages.map(({ to, iconId, labelText }) => ( + + ))} +
+
+ + + + + + +
+
+ ); +} diff --git a/src/components/StateProvider.tsx b/src/components/StateProvider.tsx new file mode 100644 index 0000000..1ef48d7 --- /dev/null +++ b/src/components/StateProvider.tsx @@ -0,0 +1,99 @@ +import produce, * as immer from 'immer'; +import React from 'react'; + +// in logs store we update logs in place +// outside of immer produce +// this is just workaround +immer.setAutoFreeze(false); + +const { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState } = + React; + +export { immer }; + +const StateContext = createContext(null); +const DispatchContext = createContext(null); +const ActionsContext = createContext(null); + +export function useStoreState() { + return useContext(StateContext); +} + +export function useStoreDispatch() { + return useContext(DispatchContext); +} + +export function useStoreActions() { + return useContext(ActionsContext); +} + +// boundActionCreators +export default function Provider({ initialState, actions = {}, children }) { + const stateRef = useRef(initialState); + const [state, setState] = useState(initialState); + const getState = useCallback(() => stateRef.current, []); + useEffect(() => { + if (process.env.NODE_ENV === 'development') { + (window as any).getState2 = getState; + } + }, [getState]); + const dispatch = useCallback( + (actionId: string | ((a: any, b: any) => any), fn: (s: any) => void) => { + if (typeof actionId === 'function') return actionId(dispatch, getState); + + const stateNext = produce(getState(), fn); + if (stateNext !== stateRef.current) { + if (process.env.NODE_ENV === 'development') { + // eslint-disable-next-line no-console + console.log(actionId, stateNext); + } + stateRef.current = stateNext; + setState(stateNext); + } + }, + [getState] + ); + const boundActions = useMemo(() => bindActions(actions, dispatch), [actions, dispatch]); + + return ( + + + {children} + + + ); +} + +export function connect(mapStateToProps: any) { + return (Component: any) => { + const MemoComponent = memo(Component); + function Connected(props: any) { + const state = useContext(StateContext); + const dispatch = useContext(DispatchContext); + const mapped = mapStateToProps(state, props); + const nextProps = { dispatch, ...props, ...mapped }; + return ; + } + return Connected; + }; +} + +// steal from https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts +function bindAction(action: any, dispatch: any) { + return function (...args: any[]) { + return dispatch(action.apply(this, args)); + }; +} + +function bindActions(actions: any, dispatch: any) { + const boundActions = {}; + for (const key in actions) { + const action = actions[key]; + if (typeof action === 'function') { + boundActions[key] = bindAction(action, dispatch); + } else if (typeof action === 'object') { + boundActions[key] = bindActions(action, dispatch); + } + } + return boundActions; +} diff --git a/src/components/StyleGuide.tsx b/src/components/StyleGuide.tsx new file mode 100644 index 0000000..7b5cf6d --- /dev/null +++ b/src/components/StyleGuide.tsx @@ -0,0 +1,71 @@ +import React, { PureComponent } from 'react'; +import { Zap } from 'react-feather'; + +import Loading from '~/components/Loading'; + +import Button from './Button'; +import Input from './Input'; +import SwitchThemed from './SwitchThemed'; +import ToggleSwitch from './ToggleSwitch'; + +const noop = () => { + /* empty */ +}; + +const paneStyle = { + padding: '20px 0', +}; + +const optionsRule = [ + { label: 'Global', value: 'Global' }, + { label: 'Rule', value: 'Rule' }, + { label: 'Direct', value: 'Direct' }, +]; + +const Pane = ({ children, style }) =>
{children}
; + +function useToggle(initialState = false) { + const [onoff, setonoff] = React.useState(initialState); + const handleChange = React.useCallback(() => { + setonoff((x) => !x); + }, []); + return [onoff, handleChange]; +} + +function SwitchExample() { + const [checked, handleChange] = useToggle(false); + return ; +} + +class StyleGuide extends PureComponent { + render() { + return ( +
+ {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */} + + + + {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */} + + + + {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */} + + + + {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */} + +
+ ); + } +} + +export default StyleGuide; diff --git a/src/components/SvgGithub.tsx b/src/components/SvgGithub.tsx new file mode 100644 index 0000000..45828c2 --- /dev/null +++ b/src/components/SvgGithub.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +type Props = { + width?: number; + height?: number; +}; + +export default function SvgGithub({ width = 24, height = 24 }: Props = {}) { + return ( + + + + ); +} diff --git a/src/components/SvgYacd.module.scss b/src/components/SvgYacd.module.scss new file mode 100644 index 0000000..f668137 --- /dev/null +++ b/src/components/SvgYacd.module.scss @@ -0,0 +1,14 @@ +.path { + stroke-dasharray: 890; + stroke-dashoffset: 890; + animation: dash 3s ease-in-out forwards normal infinite; +} + +@keyframes dash { + from { + stroke-dashoffset: 890; + } + to { + stroke-dashoffset: 0; + } +} diff --git a/src/components/SvgYacd.tsx b/src/components/SvgYacd.tsx new file mode 100644 index 0000000..d7c1b2f --- /dev/null +++ b/src/components/SvgYacd.tsx @@ -0,0 +1,92 @@ +import cx from 'clsx'; +import * as React from 'react'; + +import s from './SvgYacd.module.scss'; + +type Props = { + width?: number; + height?: number; + animate?: boolean; + c0?: string; + c1?: string; + stroke?: string; + eye?: string; + line?: string; +}; + +function SvgYacd({ + width = 320, + height = 320, + animate = false, + c0 = '#316eb5', + c1 = '#f19500', + line = '#cccccc', +}: Props) { + const faceClasName = cx({ [s.path]: animate }); + return ( + + + + + + + + + ); +} + +export default SvgYacd; diff --git a/src/components/SwitchThemed.tsx b/src/components/SwitchThemed.tsx new file mode 100644 index 0000000..aba10d1 --- /dev/null +++ b/src/components/SwitchThemed.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import ReactSwitch from 'react-switch'; + +import { State } from '~/store/types'; + +import { getTheme } from '../store/app'; +import { connect } from './StateProvider'; + +// workaround https://github.com/vitejs/vite/issues/2139#issuecomment-802981228 +// @ts-ignore +const Switch = ReactSwitch.default ? ReactSwitch.default : ReactSwitch; + +function SwitchThemed({ checked = false, onChange, theme, name }) { + const offColor = theme === 'dark' ? '#393939' : '#e9e9e9'; + + return ( + + ); +} + +export default connect((s: State) => ({ theme: getTheme(s) }))(SwitchThemed); diff --git a/src/components/ToggleSwitch.module.scss b/src/components/ToggleSwitch.module.scss new file mode 100644 index 0000000..4b1388c --- /dev/null +++ b/src/components/ToggleSwitch.module.scss @@ -0,0 +1,39 @@ +.ToggleSwitch { + user-select: none; + border-radius: 4px; + border: 1px solid #525252; + color: var(--color-text); + background: var(--color-toggle-bg); + display: flex; + position: relative; + outline: none; + + &:focus { + border-color: var(--color-focus-blue); + } + + input { + position: absolute; + left: 0; + opacity: 0; + } + + label { + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + padding: 10px 0; + cursor: pointer; + } +} + +.slider { + z-index: 1; + position: absolute; + display: block; + left: 0; + height: 100%; + transition: left 0.2s ease-out; + background: var(--color-toggle-selected); +} diff --git a/src/components/ToggleSwitch.tsx b/src/components/ToggleSwitch.tsx new file mode 100644 index 0000000..58400c9 --- /dev/null +++ b/src/components/ToggleSwitch.tsx @@ -0,0 +1,65 @@ +import React, { useCallback, useMemo } from 'react'; + +import s0 from './ToggleSwitch.module.scss'; + +type Props = { + options?: any[]; + value?: string; + name?: string; + onChange?: (...args: any[]) => any; +}; + +function ToggleSwitch({ options, value, name, onChange }: Props) { + const idxSelected = useMemo(() => options.map((o) => o.value).indexOf(value), [options, value]); + + const getPortionPercentage = useCallback( + (idx: number) => { + const w = Math.floor(100 / options.length); + if (idx === options.length - 1) { + return 100 - options.length * w + w; + } else if (idx > -1) { + return w; + } + }, + [options] + ); + + const sliderStyle = useMemo(() => { + return { + width: getPortionPercentage(idxSelected) + '%', + left: idxSelected * getPortionPercentage(0) + '%', + }; + }, [idxSelected, getPortionPercentage]); + + return ( +
+
+ {options.map((o, idx) => { + const id = `${name}-${o.label}`; + const className = idx === 0 ? '' : 'border-left'; + return ( + + ); + })} +
+ ); +} + +export default React.memo(ToggleSwitch); diff --git a/src/components/TrafficChart.tsx b/src/components/TrafficChart.tsx new file mode 100644 index 0000000..a6fdf27 --- /dev/null +++ b/src/components/TrafficChart.tsx @@ -0,0 +1,61 @@ +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { State } from '~/store/types'; + +import { fetchData } from '../api/traffic'; +import useLineChart from '../hooks/useLineChart'; +import { chartJSResource, chartStyles, commonDataSetProps } from '../misc/chart'; +import { getClashAPIConfig, getSelectedChartStyleIndex } from '../store/app'; +import { connect } from './StateProvider'; + +const { useMemo } = React; + +const chartWrapperStyle = { + // make chartjs chart responsive + position: 'relative', + maxWidth: 1000, + marginTop: '1em', +}; + +const mapState = (s: State) => ({ + apiConfig: getClashAPIConfig(s), + selectedChartStyleIndex: getSelectedChartStyleIndex(s), +}); + +export default connect(mapState)(TrafficChart); + +function TrafficChart({ apiConfig, selectedChartStyleIndex }) { + const ChartMod = chartJSResource.read(); + const traffic = fetchData(apiConfig); + const { t } = useTranslation(); + const data = useMemo( + () => ({ + labels: traffic.labels, + datasets: [ + { + ...commonDataSetProps, + ...chartStyles[selectedChartStyleIndex].up, + label: t('Up'), + data: traffic.up, + }, + { + ...commonDataSetProps, + ...chartStyles[selectedChartStyleIndex].down, + label: t('Down'), + data: traffic.down, + }, + ], + }), + [traffic, selectedChartStyleIndex, t] + ); + + useLineChart(ChartMod.Chart, 'trafficChart', data, traffic); + + return ( + // @ts-expect-error ts-migrate(2322) FIXME: Type '{ position: string; maxWidth: number; }' is ... Remove this comment to see the full error message +
+ +
+ ); +} diff --git a/src/components/TrafficChartSample.tsx b/src/components/TrafficChartSample.tsx new file mode 100644 index 0000000..00372f0 --- /dev/null +++ b/src/components/TrafficChartSample.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; + +import useLineChart from '../hooks/useLineChart'; +import { chartJSResource, chartStyles, commonDataSetProps } from '../misc/chart'; + +const { useMemo } = React; + +const extraChartOptions: import('chart.js').ChartOptions<'line'> = { + plugins: { + legend: { display: false }, + }, + scales: { + x: { display: false, type: 'category' }, + y: { display: false, type: 'linear' }, + }, +}; + +const data1 = [23e3, 35e3, 46e3, 33e3, 90e3, 68e3, 23e3, 45e3]; +const data2 = [184e3, 183e3, 196e3, 182e3, 190e3, 186e3, 182e3, 189e3]; +const labels = data1; + +export default function TrafficChart({ id }) { + const ChartMod = chartJSResource.read(); + + const data = useMemo( + () => ({ + labels, + datasets: [ + { + ...commonDataSetProps, + ...chartStyles[id].up, + data: data1, + }, + { + ...commonDataSetProps, + ...chartStyles[id].down, + data: data2, + }, + ], + }), + [id] + ); + + const eleId = 'chart-' + id; + useLineChart(ChartMod.Chart, eleId, data, null, extraChartOptions); + + return ( +
+ +
+ ); +} diff --git a/src/components/TrafficNow.module.scss b/src/components/TrafficNow.module.scss new file mode 100644 index 0000000..a150b32 --- /dev/null +++ b/src/components/TrafficNow.module.scss @@ -0,0 +1,28 @@ +.TrafficNow { + color: var(--color-text); + display: flex; + align-items: center; + flex-wrap: wrap; + justify-content: space-between; + max-width: 1000px; + + .sec { + padding: 10px; + width: 19%; + margin: 3px; + background-color: var(--color-bg-card); + border-radius: 10px; + box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1); + div:nth-child(1) { + color: var(--color-text-secondary); + font-size: 0.65em; + } + div:nth-child(2) { + padding: 10px 0 0; + font-size: 1em; + } + @media (max-width: 768px) { + width: 48%; + } + } +} diff --git a/src/components/TrafficNow.tsx b/src/components/TrafficNow.tsx new file mode 100644 index 0000000..b82594c --- /dev/null +++ b/src/components/TrafficNow.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; + +import * as connAPI from '../api/connections'; +import { fetchData } from '../api/traffic'; +import prettyBytes from '../misc/pretty-bytes'; +import { getClashAPIConfig } from '../store/app'; +import { connect } from './StateProvider'; +import s0 from './TrafficNow.module.scss'; + +const { useState, useEffect, useCallback } = React; + +const mapState = (s) => ({ + apiConfig: getClashAPIConfig(s), +}); +export default connect(mapState)(TrafficNow); + +function TrafficNow({ apiConfig }) { + const { t } = useTranslation(); + const { upStr, downStr } = useSpeed(apiConfig); + const { upTotal, dlTotal, connNumber } = useConnection(apiConfig); + return ( +
+
+
{t('Upload')}
+
{upStr}
+
+
+
{t('Download')}
+
{downStr}
+
+
+
{t('Upload Total')}
+
{upTotal}
+
+
+
{t('Download Total')}
+
{dlTotal}
+
+
+
{t('Active Connections')}
+
{connNumber}
+
+
+ ); +} + +function useSpeed(apiConfig) { + const [speed, setSpeed] = useState({ upStr: '0 B/s', downStr: '0 B/s' }); + useEffect(() => { + return fetchData(apiConfig).subscribe((o) => + setSpeed({ + upStr: prettyBytes(o.up) + '/s', + downStr: prettyBytes(o.down) + '/s', + }) + ); + }, [apiConfig]); + return speed; +} + +function useConnection(apiConfig) { + const [state, setState] = useState({ + upTotal: '0 B', + dlTotal: '0 B', + connNumber: 0, + }); + const read = useCallback( + ({ downloadTotal, uploadTotal, connections }) => { + setState({ + upTotal: prettyBytes(uploadTotal), + dlTotal: prettyBytes(downloadTotal), + connNumber: connections.length, + }); + }, + [setState] + ); + useEffect(() => { + return connAPI.fetchData(apiConfig, read); + }, [apiConfig, read]); + return state; +} diff --git a/src/components/about/About.module.scss b/src/components/about/About.module.scss new file mode 100644 index 0000000..7ed1aa5 --- /dev/null +++ b/src/components/about/About.module.scss @@ -0,0 +1,20 @@ +@import '~/styles/utils/custom-media'; + +.root { + padding: 6px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} + +.mono { + font-family: var(--font-mono); +} + +.link { + color: var(--color-text-secondary); + display: inline-flex; +} +.link:hover { + color: var(--color-text-highlight); +} diff --git a/src/components/about/About.tsx b/src/components/about/About.tsx new file mode 100644 index 0000000..383e3f7 --- /dev/null +++ b/src/components/about/About.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import { GitHub } from 'react-feather'; +import { useQuery } from 'react-query'; + +import { fetchVersion } from '~/api/version'; +import ContentHeader from '~/components/ContentHeader'; +import { connect } from '~/components/StateProvider'; +import { getClashAPIConfig } from '~/store/app'; +import { ClashAPIConfig } from '~/types'; + +import s from './About.module.scss'; + +type Props = { apiConfig: ClashAPIConfig }; + +function Version({ name, link, version }: { name: string; link: string; version: string }) { + return ( +
+

{name}

+

+ Version + {version} +

+

+ + + Source + +

+
+ ); +} + +function AboutImpl(props: Props) { + const { data: version } = useQuery(['/version', props.apiConfig], () => + fetchVersion('/version', props.apiConfig) + ); + return ( + <> + + {version && version.version ? ( + + ) : null} + + + ); +} + +const mapState = (s) => ({ + apiConfig: getClashAPIConfig(s), +}); + +export const About = connect(mapState)(AboutImpl); diff --git a/src/components/proxies/ClosePrevConns.tsx b/src/components/proxies/ClosePrevConns.tsx new file mode 100644 index 0000000..6718fb2 --- /dev/null +++ b/src/components/proxies/ClosePrevConns.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; + +import Button from '../Button'; +import { FlexCenter } from '../shared/Styled'; + +const { useRef, useEffect } = React; + +type Props = { + onClickPrimaryButton?: () => void; + onClickSecondaryButton?: () => void; +}; + +export function ClosePrevConns({ onClickPrimaryButton, onClickSecondaryButton }: Props) { + const primaryButtonRef = useRef(null); + const secondaryButtonRef = useRef(null); + useEffect(() => { + primaryButtonRef.current.focus(); + }, []); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.keyCode === 39) { + secondaryButtonRef.current.focus(); + } else if (e.keyCode === 37) { + primaryButtonRef.current.focus(); + } + }; + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
+

Close Connections?

+

+ Click 'Yes' to close those connections that are still using the old selected proxy in this + group +

+
+ + +
+ + +
+ ); +} diff --git a/src/components/proxies/Proxies.module.scss b/src/components/proxies/Proxies.module.scss new file mode 100644 index 0000000..d3295cd --- /dev/null +++ b/src/components/proxies/Proxies.module.scss @@ -0,0 +1,38 @@ +@import '~/styles/utils/custom-media'; + +.topBar { + position: sticky; + top: 0; + + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + z-index: 1; + background-color: var(--color-background2); + backdrop-filter: blur(36px); +} + +.topBarRight { + display: flex; + align-items: center; + flex-wrap: wrap; + flex: 1; + justify-content: flex-end; + + margin-right: 20px; +} + +.textFilterContainer { + max-width: 350px; + min-width: 150px; + flex: 1; + margin-right: 8px; +} + +.group { + padding: 10px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} diff --git a/src/components/proxies/Proxies.tsx b/src/components/proxies/Proxies.tsx new file mode 100644 index 0000000..7c6fd48 --- /dev/null +++ b/src/components/proxies/Proxies.tsx @@ -0,0 +1,128 @@ +import { Tooltip } from '@reach/tooltip'; +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; + +import Button from '~/components/Button'; +import ContentHeader from '~/components/ContentHeader'; +import { ClosePrevConns } from '~/components/proxies/ClosePrevConns'; +import { ProxyGroup } from '~/components/proxies/ProxyGroup'; +import { ProxyPageFab } from '~/components/proxies/ProxyPageFab'; +import { ProxyProviderList } from '~/components/proxies/ProxyProviderList'; +import Settings from '~/components/proxies/Settings'; +import BaseModal from '~/components/shared/BaseModal'; +import { TextFilter } from '~/components/shared/TextFitler'; +import { connect, useStoreActions } from '~/components/StateProvider'; +import Equalizer from '~/components/svg/Equalizer'; +import { getClashAPIConfig } from '~/store/app'; +import { + fetchProxies, + getDelay, + getProxyGroupNames, + getProxyProviders, + getShowModalClosePrevConns, + proxyFilterText, +} from '~/store/proxies'; +import type { State } from '~/store/types'; + +import s0 from './Proxies.module.scss'; + +const { useState, useEffect, useCallback, useRef } = React; + +function Proxies({ + dispatch, + groupNames, + delay, + proxyProviders, + apiConfig, + showModalClosePrevConns, +}) { + const refFetchedTimestamp = useRef<{ startAt?: number; completeAt?: number }>({}); + + const fetchProxiesHooked = useCallback(() => { + refFetchedTimestamp.current.startAt = Date.now(); + dispatch(fetchProxies(apiConfig)).then(() => { + refFetchedTimestamp.current.completeAt = Date.now(); + }); + }, [apiConfig, dispatch]); + useEffect(() => { + // fetch it now + fetchProxiesHooked(); + + // arm a window on focus listener to refresh it + const fn = () => { + if ( + refFetchedTimestamp.current.startAt && + Date.now() - refFetchedTimestamp.current.startAt > 3e4 // 30s + ) { + fetchProxiesHooked(); + } + }; + window.addEventListener('focus', fn, false); + return () => window.removeEventListener('focus', fn, false); + }, [fetchProxiesHooked]); + + const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); + const closeSettingsModal = useCallback(() => { + setIsSettingsModalOpen(false); + }, []); + + const { + proxies: { closeModalClosePrevConns, closePrevConnsAndTheModal }, + } = useStoreActions(); + + const { t } = useTranslation(); + + return ( + <> + + + +
+ +
+
+ +
+ + + +
+
+
+ {groupNames.map((groupName: string) => { + return ( +
+ +
+ ); + })} +
+ +
+ + + closePrevConnsAndTheModal(apiConfig)} + onClickSecondaryButton={closeModalClosePrevConns} + /> + + + ); +} + +const mapState = (s: State) => ({ + apiConfig: getClashAPIConfig(s), + groupNames: getProxyGroupNames(s), + proxyProviders: getProxyProviders(s), + delay: getDelay(s), + showModalClosePrevConns: getShowModalClosePrevConns(s), +}); + +export default connect(mapState)(Proxies); diff --git a/src/components/proxies/Proxy.module.scss b/src/components/proxies/Proxy.module.scss new file mode 100644 index 0000000..a4ac16c --- /dev/null +++ b/src/components/proxies/Proxy.module.scss @@ -0,0 +1,103 @@ +@import '~/styles/utils/custom-media'; + +.proxy { + padding: 5px; + position: relative; + border-radius: 8px; + overflow: hidden; + + display: flex; + flex-direction: column; + justify-content: space-between; + + outline: none; + border: 2px solid transparent; + + &:focus { + border-color: var(--color-focus-blue); + } + + @media (--breakpoint-not-small) { + border-radius: 10px; + padding: 10px; + } + + background-color: var(--color-bg-proxy); + + &.now { + background-color: var(--color-focus-blue); + color: #ddd; + } + + &.error { + opacity: 0.5; + } + &.selectable { + transition: transform 0.2s ease-in-out; + cursor: pointer; + &:hover { + border-color: hsl(0deg, 0%, var(--card-hover-border-lightness)); + } + } +} + +.proxyType { + font-family: var(--font-mono); + font-size: 0.6em; + @media (--breakpoint-not-small) { + font-size: 0.7em; + } +} +.udpType { + font-family: var(--font-mono); + font-size: 0.6em; + margin-right: 3px; + @media (--breakpoint-not-small) { + font-size: 0.7em; + } +} +.tfoType { + padding: 2px; +} +.row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.proxyName { + width: 100%; + margin-bottom: 5px; + font-size: 0.85em; + @media (--breakpoint-not-small) { + font-size: 0.85em; + } +} + +.proxySmall { + position: relative; + width: 10px; + height: 10px; + border-radius: 50%; + + .now { + position: absolute; + width: 6px; + height: 6px; + margin: auto; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 50%; + background-color: white; + } + + &.selectable { + transition: transform 0.1s ease-in-out; + cursor: pointer; + &:hover { + transform: scale(1.2); + } + } +} diff --git a/src/components/proxies/Proxy.tsx b/src/components/proxies/Proxy.tsx new file mode 100644 index 0000000..1aa6bb3 --- /dev/null +++ b/src/components/proxies/Proxy.tsx @@ -0,0 +1,231 @@ +import { TooltipPopup, useTooltip } from '@reach/tooltip'; +import cx from 'clsx'; +import * as React from 'react'; + +import { keyCodes } from '~/misc/keycode'; +import { + getLatencyTestUrl, +} from '~/store/app'; +import { ProxyItem } from '~/store/types'; + +import { getDelay, getProxies, NonProxyTypes } from '../../store/proxies'; +import { connect } from '../StateProvider'; +import s0 from './Proxy.module.scss'; +import { ProxyLatency } from './ProxyLatency'; + +const { useMemo } = React; + +const colorMap = { + // green + good: '#67c23a', + // yellow + normal: '#d4b75c', + // orange + bad: '#e67f3c', + // bad: '#F56C6C', + na: '#909399', +}; + +function getLabelColor({ + number, +}: { + number?: number; +} = {}, + httpsTest: boolean) { + const delayMap = { + good: httpsTest ? 800 : 200, + normal: httpsTest ? 1500 : 500, + }; + if (number === 0) { + return colorMap.na; + } else if (number < delayMap.good) { + return colorMap.good; + } else if (number < delayMap.normal) { + return colorMap.normal; + } else if (typeof number === 'number') { + return colorMap.bad; + } + return colorMap.na; +} + +function getProxyDotBackgroundColor( + latency: { + number?: number; + }, + proxyType: string, + httpsTest: boolean, +) { + if (NonProxyTypes.indexOf(proxyType) > -1) { + return 'linear-gradient(135deg, white 15%, #999 15% 30%, white 30% 45%, #999 45% 60%, white 60% 75%, #999 75% 90%, white 90% 100%)'; + } + return getLabelColor(latency, httpsTest); +} + +type ProxyProps = { + name: string; + now?: boolean; + proxy: ProxyItem; + latency: any; + httpsLatencyTest: boolean, + isSelectable?: boolean; + udp: boolean; + tfo: boolean; + onClick?: (proxyName: string) => unknown; +}; + +function ProxySmallImpl({ now, name, proxy, latency, httpsLatencyTest, isSelectable, onClick }: ProxyProps) { + const color = useMemo(() => getProxyDotBackgroundColor(latency, proxy.type, httpsLatencyTest), [latency, proxy]); + const title = useMemo(() => { + let ret = name; + if (latency && typeof latency.number === 'number') { + ret += ' ' + latency.number + ' ms'; + } + return ret; + }, [name, latency]); + + const doSelect = React.useCallback(() => { + isSelectable && onClick && onClick(name); + }, [name, onClick, isSelectable]); + + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + if (e.keyCode === keyCodes.Enter) { + doSelect(); + } + }, + [doSelect] + ); + + return ( +
+ {now &&
} +
+ ); +} + +function formatProxyType(t: string) { + if (t === 'Shadowsocks') return 'SS'; + return t; +} + +const positionProxyNameTooltip = (triggerRect: { left: number; top: number }) => { + return { + left: triggerRect.left + window.scrollX - 5, + top: triggerRect.top + window.scrollY - 38, + }; +}; + +function ProxyNameTooltip({ children, label, 'aria-label': ariaLabel }) { + const [trigger, tooltip] = useTooltip(); + return ( + <> + {React.cloneElement(children, trigger)} + + + ); +} + +function ProxyImpl({ now, name, proxy, latency, httpsLatencyTest, isSelectable, onClick }: ProxyProps) { + const color = useMemo(() => getLabelColor(latency, httpsLatencyTest), [latency]); + const doSelect = React.useCallback(() => { + isSelectable && onClick && onClick(name); + }, [name, onClick, isSelectable]); + function formatUdpType(udp: boolean, xudp?: boolean) { + if (!udp) return ''; + return xudp ? 'XUDP' : 'UDP'; + } + function formatTfo(t: boolean) { + if (!t) return ''; + return ( + + + + ); + } + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + if (e.keyCode === keyCodes.Enter) { + doSelect(); + } + }, + [doSelect] + ); + const className = useMemo(() => { + return cx(s0.proxy, { + [s0.now]: now, + [s0.error]: latency && latency.error, + [s0.selectable]: isSelectable, + }); + }, [isSelectable, now, latency]); + + const latencyNumber = latency?.number ?? proxy.history[proxy.history.length - 1]?.delay; + + return ( +
+
+ + {name} + + + {formatUdpType(proxy.udp, proxy.xudp)} + +
+ +
+
+ + {formatProxyType(proxy.type)} + + + {formatTfo(proxy.tfo)} +
+ + {latencyNumber ? : null} +
+
+ ); +} + +const mapState = (s: any, { name }) => { + const proxies = getProxies(s); + const delay = getDelay(s); + const latencyTestUrl = getLatencyTestUrl(s); + return { + proxy: proxies[name], + latency: delay[name], + httpsLatencyTest: latencyTestUrl.startsWith('https://'), + }; +}; + +export const Proxy = connect(mapState)(ProxyImpl); +export const ProxySmall = connect(mapState)(ProxySmallImpl); diff --git a/src/components/proxies/ProxyGroup.module.scss b/src/components/proxies/ProxyGroup.module.scss new file mode 100644 index 0000000..5409ea8 --- /dev/null +++ b/src/components/proxies/ProxyGroup.module.scss @@ -0,0 +1,11 @@ +.header { + margin-bottom: 12px; +} + +.zapWrapper { + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/components/proxies/ProxyGroup.tsx b/src/components/proxies/ProxyGroup.tsx new file mode 100644 index 0000000..fb97e28 --- /dev/null +++ b/src/components/proxies/ProxyGroup.tsx @@ -0,0 +1,137 @@ +import * as React from 'react'; +import { Zap } from 'react-feather'; +import { useQuery } from 'react-query'; + +import * as proxiesAPI from '~/api/proxies'; +import { fetchVersion } from '~/api/version'; +import { + getCollapsibleIsOpen, + getHideUnavailableProxies, + getLatencyTestUrl, + getProxySortBy, +} from '~/store/app'; +import { fetchProxies, getProxies, switchProxy } from '~/store/proxies'; + +import Button from '../Button'; +import CollapsibleSectionHeader from '../CollapsibleSectionHeader'; +import { connect, useStoreActions } from '../StateProvider'; +import { useFilteredAndSorted } from './hooks'; +import s0 from './ProxyGroup.module.scss'; +import { ProxyList, ProxyListSummaryView } from './ProxyList'; + +const { createElement, useCallback, useMemo, useState } = React; + +function ZapWrapper() { + return ( +
+ +
+ ); +} + +function ProxyGroupImpl({ + name, + all: allItems, + delay, + hideUnavailableProxies, + proxySortBy, + proxies, + type, + now, + isOpen, + latencyTestUrl, + apiConfig, + dispatch, +}) { + const all = useFilteredAndSorted(allItems, delay, hideUnavailableProxies, proxySortBy, proxies); + + const { data: version } = useQuery(['/version', apiConfig], () => + fetchVersion('/version', apiConfig) + ); + + const isSelectable = useMemo( + () => ['Selector', version.meta && 'Fallback'].includes(type), + [type, version.meta] + ); + + const { + app: { updateCollapsibleIsOpen }, + proxies: { requestDelayForProxies }, + } = useStoreActions(); + + const toggle = useCallback(() => { + updateCollapsibleIsOpen('proxyGroup', name, !isOpen); + }, [isOpen, updateCollapsibleIsOpen, name]); + + const itemOnTapCallback = useCallback( + (proxyName) => { + if (!isSelectable) return; + dispatch(switchProxy(apiConfig, name, proxyName)); + }, + [apiConfig, dispatch, name, isSelectable] + ); + const [isTestingLatency, setIsTestingLatency] = useState(false); + const testLatency = useCallback(async () => { + setIsTestingLatency(true); + try { + if (version.meta === true) { + await proxiesAPI.requestDelayForProxyGroup(apiConfig, name, latencyTestUrl); + await dispatch(fetchProxies(apiConfig)); + } else { + await requestDelayForProxies(apiConfig, all); + await dispatch(fetchProxies(apiConfig)); + } + } catch (err) {} + setIsTestingLatency(false); + }, [all, apiConfig, dispatch, name, version.meta]); + + return ( +
+
+ + +
+ {createElement(isOpen ? ProxyList : ProxyListSummaryView, { + all, + now, + isSelectable, + itemOnTapCallback, + })} +
+ ); +} + +export const ProxyGroup = connect((s, { name, delay }) => { + const proxies = getProxies(s); + const collapsibleIsOpen = getCollapsibleIsOpen(s); + const proxySortBy = getProxySortBy(s); + const hideUnavailableProxies = getHideUnavailableProxies(s); + const latencyTestUrl = getLatencyTestUrl(s); + + const group = proxies[name]; + const { all, type, now } = group; + return { + all, + delay, + hideUnavailableProxies, + proxySortBy, + proxies, + type, + now, + isOpen: collapsibleIsOpen[`proxyGroup:${name}`], + latencyTestUrl, + }; +})(ProxyGroupImpl); diff --git a/src/components/proxies/ProxyLatency.module.scss b/src/components/proxies/ProxyLatency.module.scss new file mode 100644 index 0000000..e5a5b3f --- /dev/null +++ b/src/components/proxies/ProxyLatency.module.scss @@ -0,0 +1,10 @@ +@import '~/styles/utils/custom-media'; + +.proxyLatency { + border-radius: 20px; + color: #eee; + font-size: 0.6em; + @media (--breakpoint-not-small) { + font-size: 0.7em; + } +} diff --git a/src/components/proxies/ProxyLatency.tsx b/src/components/proxies/ProxyLatency.tsx new file mode 100644 index 0000000..48e55af --- /dev/null +++ b/src/components/proxies/ProxyLatency.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +import s0 from './ProxyLatency.module.scss'; + +type ProxyLatencyProps = { + number: number; + color: string; +}; + +export function ProxyLatency({ number, color }: ProxyLatencyProps) { + return ( + + {number} ms + + ); +} diff --git a/src/components/proxies/ProxyList.module.scss b/src/components/proxies/ProxyList.module.scss new file mode 100644 index 0000000..33f76f0 --- /dev/null +++ b/src/components/proxies/ProxyList.module.scss @@ -0,0 +1,19 @@ +@import '~/styles/utils/custom-media'; + +.list { + margin: 8px 0; + display: grid; + grid-gap: 10px; +} + +.detail { + grid-template-columns: auto auto; + + @media (--breakpoint-not-small) { + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + } +} + +.summary { + grid-template-columns: repeat(auto-fill, 12px); +} diff --git a/src/components/proxies/ProxyList.tsx b/src/components/proxies/ProxyList.tsx new file mode 100644 index 0000000..913e49b --- /dev/null +++ b/src/components/proxies/ProxyList.tsx @@ -0,0 +1,56 @@ +import cx from 'clsx'; +import * as React from 'react'; + +import { Proxy, ProxySmall } from './Proxy'; +import s from './ProxyList.module.scss'; + +type ProxyListProps = { + all: string[]; + now?: string; + isSelectable?: boolean; + itemOnTapCallback?: (x: string) => void; + show?: boolean; +}; + +export function ProxyList({ all, now, isSelectable, itemOnTapCallback }: ProxyListProps) { + const proxies = all; + + return ( +
+ {proxies.map((proxyName) => { + return ( + + ); + })} +
+ ); +} + +export function ProxyListSummaryView({ + all, + now, + isSelectable, + itemOnTapCallback, +}: ProxyListProps) { + return ( +
+ {all.map((proxyName) => { + return ( + + ); + })} +
+ ); +} diff --git a/src/components/proxies/ProxyPageFab.tsx b/src/components/proxies/ProxyPageFab.tsx new file mode 100644 index 0000000..2adb0a5 --- /dev/null +++ b/src/components/proxies/ProxyPageFab.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import { Zap } from 'react-feather'; +import { useTranslation } from 'react-i18next'; + +import { useUpdateProviderItems } from '~/components/proxies/proxies.hooks'; +import { Action, Fab, IsFetching, position as fabPosition } from '~/components/shared/Fab'; +import { RotateIcon } from '~/components/shared/RotateIcon'; +import { requestDelayAll } from '~/store/proxies'; +import { DispatchFn, FormattedProxyProvider } from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +const { useState, useCallback } = React; + +function StatefulZap({ isLoading }: { isLoading: boolean }) { + return isLoading ? ( + + + + ) : ( + + ); +} + +function useTestLatencyAction({ + dispatch, + apiConfig, +}: { + dispatch: DispatchFn; + apiConfig: ClashAPIConfig; +}): [() => unknown, boolean] { + const [isTestingLatency, setIsTestingLatency] = useState(false); + const requestDelayAllFn = useCallback(() => { + if (isTestingLatency) return; + + setIsTestingLatency(true); + dispatch(requestDelayAll(apiConfig)).then( + () => setIsTestingLatency(false), + () => setIsTestingLatency(false) + ); + }, [apiConfig, dispatch, isTestingLatency]); + return [requestDelayAllFn, isTestingLatency]; +} + +export function ProxyPageFab({ + dispatch, + apiConfig, + proxyProviders, +}: { + dispatch: DispatchFn; + apiConfig: ClashAPIConfig; + proxyProviders: FormattedProxyProvider[]; +}) { + const { t } = useTranslation(); + const [requestDelayAllFn, isTestingLatency] = useTestLatencyAction({ + dispatch, + apiConfig, + }); + + const [updateProviders, isUpdating] = useUpdateProviderItems({ + apiConfig, + dispatch, + names: proxyProviders.map((item) => item.name), + }); + + return ( + } + onClick={requestDelayAllFn} + text={t('Test Latency')} + style={fabPosition} + > + {proxyProviders.length > 0 ? ( + + + + ) : null} + + ); +} diff --git a/src/components/proxies/ProxyProvider.module.scss b/src/components/proxies/ProxyProvider.module.scss new file mode 100644 index 0000000..223b089 --- /dev/null +++ b/src/components/proxies/ProxyProvider.module.scss @@ -0,0 +1,32 @@ +@import '~/styles/utils/custom-media'; + +.updatedAt { + margin-bottom: 12px; + small { + color: #777; + } +} + +.body { + padding: 10px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} + +.actionFooter { + display: flex; + button { + margin: 0 5px; + &:first-child { + margin-left: 0; + } + } +} + +.refresh { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; +} diff --git a/src/components/proxies/ProxyProvider.tsx b/src/components/proxies/ProxyProvider.tsx new file mode 100644 index 0000000..289293c --- /dev/null +++ b/src/components/proxies/ProxyProvider.tsx @@ -0,0 +1,197 @@ +import { formatDistance } from 'date-fns'; +import * as React from 'react'; +import { RotateCw, Zap } from 'react-feather'; + +import Button from '~/components/Button'; +import Collapsible from '~/components/Collapsible'; +import CollapsibleSectionHeader from '~/components/CollapsibleSectionHeader'; +import { useUpdateProviderItem } from '~/components/proxies/proxies.hooks'; +import { connect, useStoreActions } from '~/components/StateProvider'; +import { framerMotionResouce } from '~/misc/motion'; +import { + getClashAPIConfig, + getCollapsibleIsOpen, + getHideUnavailableProxies, + getProxySortBy, +} from '~/store/app'; +import { getDelay, healthcheckProviderByName } from '~/store/proxies'; +import { DelayMapping, SubscriptionInfo } from '~/store/types'; + +import { useFilteredAndSorted } from './hooks'; +import { ProxyList, ProxyListSummaryView } from './ProxyList'; +import s from './ProxyProvider.module.scss'; + +const { useState, useCallback } = React; + +type Props = { + name: string; + proxies: Array; + delay: DelayMapping; + hideUnavailableProxies: boolean; + proxySortBy: string; + type: 'Proxy' | 'Rule'; + vehicleType: 'HTTP' | 'File' | 'Compatible'; + updatedAt?: string; + subscriptionInfo?: SubscriptionInfo; + dispatch: (x: any) => Promise; + isOpen: boolean; + apiConfig: any; +}; + +function ProxyProviderImpl({ + name, + proxies: all, + delay, + hideUnavailableProxies, + proxySortBy, + vehicleType, + updatedAt, + subscriptionInfo, + isOpen, + dispatch, + apiConfig, +}: Props) { + const proxies = useFilteredAndSorted(all, delay, hideUnavailableProxies, proxySortBy); + const [isHealthcheckLoading, setIsHealthcheckLoading] = useState(false); + + const updateProvider = useUpdateProviderItem({ dispatch, apiConfig, name }); + + const healthcheckProvider = useCallback(async () => { + setIsHealthcheckLoading(true); + await dispatch(healthcheckProviderByName(apiConfig, name)); + setIsHealthcheckLoading(false); + }, [apiConfig, dispatch, name, setIsHealthcheckLoading]); + + const { + app: { updateCollapsibleIsOpen }, + } = useStoreActions(); + + const toggle = useCallback(() => { + updateCollapsibleIsOpen('proxyProvider', name, !isOpen); + }, [isOpen, updateCollapsibleIsOpen, name]); + + const timeAgo = formatDistance(new Date(updatedAt), new Date()); + const total = subscriptionInfo ? formatBytes(subscriptionInfo.Total) : 0; + const used = subscriptionInfo + ? formatBytes(subscriptionInfo.Download + subscriptionInfo.Upload) + : 0; + const percentage = subscriptionInfo + ? ( + ((subscriptionInfo.Download + subscriptionInfo.Upload) / subscriptionInfo.Total) * + 100 + ).toFixed(2) + : 0; + const expireStr = () => { + if (subscriptionInfo.Expire === 0) { + return 'Null'; + } + const expire = new Date(subscriptionInfo.Expire * 1000); + const getYear = expire.getFullYear() + '-'; + const getMonth = + (expire.getMonth() + 1 < 10 ? '0' + (expire.getMonth() + 1) : expire.getMonth() + 1) + '-'; + const getDate = expire.getDate() + ' '; + return getYear + getMonth + getDate; + }; + return ( +
+
+ +
+ {subscriptionInfo && ( +
+ + {used} / {total} ( {percentage}% )    Expire: {expireStr()}{' '} + +
+ )} +
+ Updated {timeAgo} ago +
+ {/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element[]; isOpen: boolean; }' i... Remove this comment to see the full error message */} + + +
+
+
+ {/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element; isOpen: boolean; }' is ... Remove this comment to see the full error message */} + + + +
+ ); +} + +const button = { + rest: { scale: 1 }, + pressed: { scale: 0.95 }, +}; +const arrow = { + rest: { rotate: 0 }, + hover: { rotate: 360, transition: { duration: 0.3 } }, +}; + +function formatBytes(bytes, decimals = 2) { + if (!+bytes) return '0 Bytes'; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; +} +function Refresh() { + const module = framerMotionResouce.read(); + const motion = module.motion; + return ( + + + + + + ); +} + +const mapState = (s, { proxies, name }) => { + const hideUnavailableProxies = getHideUnavailableProxies(s); + const delay = getDelay(s); + const collapsibleIsOpen = getCollapsibleIsOpen(s); + const apiConfig = getClashAPIConfig(s); + + const proxySortBy = getProxySortBy(s); + + return { + apiConfig, + proxies, + delay, + hideUnavailableProxies, + proxySortBy, + isOpen: collapsibleIsOpen[`proxyProvider:${name}`], + }; +}; + +export const ProxyProvider = connect(mapState)(ProxyProviderImpl); diff --git a/src/components/proxies/ProxyProviderList.tsx b/src/components/proxies/ProxyProviderList.tsx new file mode 100644 index 0000000..ae79ea8 --- /dev/null +++ b/src/components/proxies/ProxyProviderList.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +import ContentHeader from '~/components/ContentHeader'; +import { ProxyProvider } from '~/components/proxies/ProxyProvider'; +import { FormattedProxyProvider } from '~/store/types'; + +export function ProxyProviderList({ items }: { items: FormattedProxyProvider[] }) { + if (items.length === 0) return null; + return ( + <> + +
+ {items.map((item) => ( + + ))} +
+ + ); +} diff --git a/src/components/proxies/Settings.module.scss b/src/components/proxies/Settings.module.scss new file mode 100644 index 0000000..364d07d --- /dev/null +++ b/src/components/proxies/Settings.module.scss @@ -0,0 +1,17 @@ +.labeledInput { + max-width: 85vw; + width: 400px; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 13px; + padding: 13px 0; +} + +hr { + height: 1px; + background-color: var(--color-separator); + border: none; + outline: none; + margin: 1rem 0px; +} diff --git a/src/components/proxies/Settings.tsx b/src/components/proxies/Settings.tsx new file mode 100644 index 0000000..b2ec192 --- /dev/null +++ b/src/components/proxies/Settings.tsx @@ -0,0 +1,92 @@ +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; + +import Select from '~/components/shared/Select'; + +import { getAutoCloseOldConns, getHideUnavailableProxies, getProxySortBy } from '../../store/app'; +import { connect, useStoreActions } from '../StateProvider'; +import Switch from '../SwitchThemed'; +import s from './Settings.module.scss'; + +const options = [ + ['Natural', 'order_natural'], + ['LatencyAsc', 'order_latency_asc'], + ['LatencyDesc', 'order_latency_desc'], + ['NameAsc', 'order_name_asc'], + ['NameDesc', 'order_name_desc'], +]; + +const { useCallback } = React; + +function Settings({ appConfig }) { + const { + app: { updateAppConfig }, + } = useStoreActions(); + + const handleProxySortByOnChange = useCallback( + (e) => { + updateAppConfig('proxySortBy', e.target.value); + }, + [updateAppConfig] + ); + + const handleHideUnavailablesSwitchOnChange = useCallback( + (v) => { + updateAppConfig('hideUnavailableProxies', v); + }, + [updateAppConfig] + ); + const { t } = useTranslation(); + return ( + <> +
+ {t('sort_in_grp')} +
+ + {options.map(([value, name]) => ( + + ))} + + ); +} diff --git a/src/components/shared/Styled.module.scss b/src/components/shared/Styled.module.scss new file mode 100644 index 0000000..88b84ad --- /dev/null +++ b/src/components/shared/Styled.module.scss @@ -0,0 +1,5 @@ +.FlexCenter { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/components/shared/Styled.tsx b/src/components/shared/Styled.tsx new file mode 100644 index 0000000..86d87ca --- /dev/null +++ b/src/components/shared/Styled.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +import s from './Styled.module.scss'; + +export function FlexCenter({ children }: { children: React.ReactNode }) { + return
{children}
; +} diff --git a/src/components/shared/TextFitler.module.scss b/src/components/shared/TextFitler.module.scss new file mode 100644 index 0000000..7d7ba9b --- /dev/null +++ b/src/components/shared/TextFitler.module.scss @@ -0,0 +1,19 @@ +.input { + -webkit-appearance: none; + background-color: var(--color-input-bg); + background-image: none; + border-radius: 20px; + border: 1px solid var(--color-input-border); + box-sizing: border-box; + color: #c1c1c1; + display: inline-block; + font-size: inherit; + outline: none; + padding: 8px 15px; + transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); + width: 100%; + + &:focus { + border: 1px solid var(--color-focus-blue); + } +} diff --git a/src/components/shared/TextFitler.tsx b/src/components/shared/TextFitler.tsx new file mode 100644 index 0000000..1a78ca2 --- /dev/null +++ b/src/components/shared/TextFitler.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import type { RecoilState } from 'recoil'; + +import { useTextInut } from '~/hooks/useTextInput'; + +import s from './TextFitler.module.scss'; + +export function TextFilter(props: { textAtom: RecoilState; placeholder?: string }) { + const [onChange, text] = useTextInut(props.textAtom); + return ( + + ); +} diff --git a/src/components/shared/ThemeSwitcher.module.scss b/src/components/shared/ThemeSwitcher.module.scss new file mode 100644 index 0000000..d2f296a --- /dev/null +++ b/src/components/shared/ThemeSwitcher.module.scss @@ -0,0 +1,55 @@ +.iconWrapper { + --sz: 40px; + + width: var(--sz); + height: var(--sz); + display: flex; + justify-content: center; + align-items: center; + + outline: none; + padding: 5px; + color: var(--color-text); +} +.iconWrapper:hover { + opacity: 0.6; +} +.iconWrapper:focus { + border-color: var(--color-focus-blue); +} + +.themeSwitchContainer { + --sz: 40px; + + position: relative; + display: flex; + align-items: center; + height: var(--sz); + select { + cursor: pointer; + padding-left: var(--sz); + width: var(--sz); + height: var(--sz); + appearance: none; + outline: none; + border-radius: 100%; + border: 1px solid transparent; + background: var(--color-bg-sidebar); + &:focus { + border-color: var(--color-focus-blue); + } + option { + // this has effect in Firefox + // Chrome and Safari use the native menu + background: var(--color-bg-sidebar); + } + } + .iconWrapper { + pointer-events: none; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + } +} diff --git a/src/components/shared/ThemeSwitcher.tsx b/src/components/shared/ThemeSwitcher.tsx new file mode 100644 index 0000000..363d422 --- /dev/null +++ b/src/components/shared/ThemeSwitcher.tsx @@ -0,0 +1,138 @@ +import { Tooltip } from '@reach/tooltip'; +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { connect } from '~/components/StateProvider'; +import { framerMotionResouce } from '~/misc/motion'; +import { getTheme, switchTheme } from '~/store/app'; +import { State } from '~/store/types'; + +import s from './ThemeSwitcher.module.scss'; + +export function ThemeSwitcherImpl({ theme, dispatch }) { + const { t } = useTranslation(); + + const themeIcon = React.useMemo(() => { + switch (theme) { + case 'dark': + return ; + case 'auto': + return ; + case 'light': + return ; + default: + console.assert(false, 'Unknown theme'); + return ; + } + }, [theme]); + + const onChange = React.useCallback( + (e: React.ChangeEvent) => dispatch(switchTheme(e.target.value)), + [dispatch] + ); + + return ( + +
+ {themeIcon} + +
+
+ ); +} + +function MoonA() { + const module = framerMotionResouce.read(); + const motion = module.motion; + return ( + + + + ); +} + +function Sun() { + const module = framerMotionResouce.read(); + const motion = module.motion; + + return ( + + + + + + + + + + + + + + ); +} + +function Auto() { + const module = framerMotionResouce.read(); + const motion = module.motion; + + return ( + + + + + + + + ); +} + +const mapState = (s: State) => ({ theme: getTheme(s) }); +export const ThemeSwitcher = connect(mapState)(ThemeSwitcherImpl); diff --git a/src/components/shared/rtf.css b/src/components/shared/rtf.css new file mode 100644 index 0000000..574aad1 --- /dev/null +++ b/src/components/shared/rtf.css @@ -0,0 +1,233 @@ +/* + * for react-tiny-fab + * based on react-tiny-fab/dist/styles.css + */ +.rtf { + box-sizing: border-box; + margin: 25px; + position: fixed; + white-space: nowrap; + z-index: 9998; + padding-left: 0; + list-style: none; +} +.rtf.open .rtf--mb { + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), + 0px 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.rtf.open .rtf--mb > ul { + list-style: none; + margin: 0; + padding: 0; +} + +.rtf.open .rtf--ab__c:hover > span { + transition: ease-in-out opacity 0.2s; + opacity: 0.9; +} +.rtf.open .rtf--ab__c > span.always-show { + transition: ease-in-out opacity 0.2s; + opacity: 0.9; +} +.rtf.open .rtf--ab__c:nth-child(1) { + transform: translateY(-60px) scale(1); + transition-delay: 0.03s; +} +.rtf.open .rtf--ab__c:nth-child(1).top { + transform: translateY(60px) scale(1); +} +.rtf.open .rtf--ab__c:nth-child(2) { + transform: translateY(-120px) scale(1); + transition-delay: 0.09s; +} +.rtf.open .rtf--ab__c:nth-child(2).top { + transform: translateY(120px) scale(1); +} +.rtf.open .rtf--ab__c:nth-child(3) { + transform: translateY(-180px) scale(1); + transition-delay: 0.12s; +} +.rtf.open .rtf--ab__c:nth-child(3).top { + transform: translateY(180px) scale(1); +} +.rtf.open .rtf--ab__c:nth-child(4) { + transform: translateY(-240px) scale(1); + transition-delay: 0.15s; +} +.rtf.open .rtf--ab__c:nth-child(4).top { + transform: translateY(240px) scale(1); +} +.rtf.open .rtf--ab__c:nth-child(5) { + transform: translateY(-300px) scale(1); + transition-delay: 0.18s; +} +.rtf.open .rtf--ab__c:nth-child(5).top { + transform: translateY(300px) scale(1); +} +.rtf.open .rtf--ab__c:nth-child(6) { + transform: translateY(-360px) scale(1); + transition-delay: 0.21s; +} +.rtf.open .rtf--ab__c:nth-child(6).top { + transform: translateY(360px) scale(1); +} + +.rtf--mb__c { + padding: 25px; + margin: -25px; +} +.rtf--mb__c *:last-child { + margin-bottom: 0; +} +.rtf--mb__c:hover > span { + transition: ease-in-out opacity 0.2s; + opacity: 0.9; +} +.rtf--mb__c > span.always-show { + transition: ease-in-out opacity 0.2s; + opacity: 0.9; +} +.rtf--mb__c > span { + opacity: 0; + transition: ease-in-out opacity 0.2s; + position: absolute; + top: 50%; + transform: translateY(-50%); + margin-right: 6px; + margin-left: 4px; + background: rgba(0, 0, 0, 0.75); + padding: 2px 4px; + border-radius: 2px; + color: white; + font-size: 13px; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28); +} +.rtf--mb__c > span.right { + right: 100%; +} + +.rtf--mb { + width: 48px; + height: 48px; + background: var(--btn-bg); + z-index: 9999; + display: inline-flex; + justify-content: center; + align-items: center; + position: relative; + border: none; + border-radius: 50%; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28); + cursor: pointer; + outline: none; + padding: 0; + -webkit-user-drag: none; + font-weight: bold; + color: #f1f1f1; + font-size: 18px; +} +.rtf--mb > * { + transition: ease-in-out transform 0.2s; +} + +.rtf--ab__c { + display: block; + position: absolute; + top: 0; + right: 1px; + padding: 10px 0; + margin: -10px 0; + transition: ease-in-out transform 0.2s; +} +.rtf--ab__c > span { + opacity: 0; + transition: ease-in-out opacity 0.2s; + position: absolute; + top: 50%; + transform: translateY(-50%); + margin-right: 6px; + background: rgba(0, 0, 0, 0.75); + padding: 2px 4px; + border-radius: 2px; + color: white; + font-size: 13px; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28); +} +.rtf--ab__c > span.right { + right: 100%; +} +.rtf--ab__c:nth-child(1) { + transform: translateY(-60px) scale(0); + transition-delay: 0.21s; +} +.rtf--ab__c:nth-child(1).top { + transform: translateY(60px) scale(0); +} +.rtf--ab__c:nth-child(2) { + transform: translateY(-120px) scale(0); + transition-delay: 0.18s; +} +.rtf--ab__c:nth-child(2).top { + transform: translateY(120px) scale(0); +} +.rtf--ab__c:nth-child(3) { + transform: translateY(-180px) scale(0); + transition-delay: 0.15s; +} +.rtf--ab__c:nth-child(3).top { + transform: translateY(180px) scale(0); +} +.rtf--ab__c:nth-child(4) { + transform: translateY(-240px) scale(0); + transition-delay: 0.12s; +} +.rtf--ab__c:nth-child(4).top { + transform: translateY(240px) scale(0); +} +.rtf--ab__c:nth-child(5) { + transform: translateY(-300px) scale(0); + transition-delay: 0.09s; +} +.rtf--ab__c:nth-child(5).top { + transform: translateY(300px) scale(0); +} +.rtf--ab__c:nth-child(6) { + transform: translateY(-360px) scale(0); + transition-delay: 0.03s; +} +.rtf--ab__c:nth-child(6).top { + transform: translateY(360px) scale(0); +} + +.rtf--ab { + height: 40px; + width: 40px; + margin-right: 4px; + background-color: #aaaaaa; + display: inline-flex; + justify-content: center; + align-items: center; + position: relative; + border: none; + border-radius: 50%; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28); + cursor: pointer; + outline: none; + padding: 0; + -webkit-user-drag: none; + font-weight: bold; + color: #f1f1f1; + font-size: 16px; + z-index: 10000; +} + +.rtf--ab:hover { + background: #387cec; + border: 1px solid #387cec; + color: #fff; +} + +.rtf--ab:focus { + border-color: var(--color-focus-blue); +} diff --git a/src/components/svg/Equalizer.tsx b/src/components/svg/Equalizer.tsx new file mode 100644 index 0000000..ae3c858 --- /dev/null +++ b/src/components/svg/Equalizer.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +type Props = { + size?: number; + color?: string; +}; + +export default function Equalizer({ color = 'currentColor', size = 24 }: Props) { + return ( + + + + + + + ); +} diff --git a/src/custom.d.ts b/src/custom.d.ts new file mode 100644 index 0000000..8e12742 --- /dev/null +++ b/src/custom.d.ts @@ -0,0 +1,69 @@ +/// +/// + +// for css modules +declare module '*.module.css' { + const classes: { [key: string]: string }; + export default classes; +} +declare module '*.module.scss' { + const classes: { [key: string]: string }; + export default classes; +} + +interface Window { + i18n: any; +} + +// webpack definePlugin replacing variables +declare const __VERSION__: string; +declare const process = { + env: { + NODE_ENV: string, + PUBLIC_URL: string, + }, +}; + +declare module 'react-table' { + interface TableOptions {} + + interface Empty {} + + interface SortByToggleProps {} + + interface Header { + getHeaderProps(p: SortByToggleProps): { role?: string }; + getSortByToggleProps(): SortByToggleProps; + render(x: string): string; + isSorted: boolean; + isSortedDesc: boolean; + } + + interface HeaderGroup { + getHeaderGroupProps(): { role?: string }; + headers: Header[]; + } + + interface Cell { + getCellProps(): { role?: string }; + + column: { id: string }; + value: number; + } + + interface Row { + cells: Cell[]; + } + + export function useTable( + options: TableOptions, + useSortBy: useSortBy + ): { + headerGroups: HeaderGroup[]; + getTableProps(): { role?: string }; + rows: Row[]; + prepareRow(r: Row): void; + }; + + export function useSortBy(): Empty; +} diff --git a/src/hooks/basic.ts b/src/hooks/basic.ts new file mode 100644 index 0000000..587d92d --- /dev/null +++ b/src/hooks/basic.ts @@ -0,0 +1,9 @@ +import React from 'react'; + +const { useState, useCallback } = React; + +export function useToggle(initialValue = false) { + const [isOn, setState] = useState(initialValue); + const toggle = useCallback(() => setState((x) => !x), []); + return [isOn, toggle]; +} diff --git a/src/hooks/useLineChart.ts b/src/hooks/useLineChart.ts new file mode 100644 index 0000000..88ee660 --- /dev/null +++ b/src/hooks/useLineChart.ts @@ -0,0 +1,25 @@ +import type { ChartConfiguration } from 'chart.js'; +import React from 'react'; + +import { commonChartOptions } from '~/misc/chart'; + +const { useEffect } = React; + +export default function useLineChart( + chart: typeof import('chart.js').Chart, + elementId: string, + data: ChartConfiguration['data'], + subscription: any, + extraChartOptions = {} +) { + useEffect(() => { + const ctx = (document.getElementById(elementId) as HTMLCanvasElement).getContext('2d'); + const options = { ...commonChartOptions, ...extraChartOptions }; + const c = new chart(ctx, { type: 'line', data, options }); + const unsubscribe = subscription && subscription.subscribe(() => c.update()); + return () => { + unsubscribe && unsubscribe(); + c.destroy(); + }; + }, [chart, elementId, data, subscription, extraChartOptions]); +} diff --git a/src/hooks/useRemainingViewPortHeight.ts b/src/hooks/useRemainingViewPortHeight.ts new file mode 100644 index 0000000..2c920c2 --- /dev/null +++ b/src/hooks/useRemainingViewPortHeight.ts @@ -0,0 +1,32 @@ +import * as React from 'react'; + +const { useState, useRef, useCallback, useLayoutEffect } = React; + +/** + * cosnt [ref, remainingHeight] = useRemainingViewPortHeight(); + * + * return a reference, and the remaining height of the referenced dom node + * to the bottom of the view port + * + */ +export default function useRemainingViewPortHeight(): [ + React.MutableRefObject, + number +] { + const ref = useRef(null); + const [containerHeight, setContainerHeight] = useState(200); + const updateContainerHeight = useCallback(() => { + const { top } = ref.current.getBoundingClientRect(); + setContainerHeight(window.innerHeight - top); + }, []); + + useLayoutEffect(() => { + updateContainerHeight(); + window.addEventListener('resize', updateContainerHeight); + return () => { + window.removeEventListener('resize', updateContainerHeight); + }; + }, [updateContainerHeight]); + + return [ref, containerHeight]; +} diff --git a/src/hooks/useTextInput.ts b/src/hooks/useTextInput.ts new file mode 100644 index 0000000..853044c --- /dev/null +++ b/src/hooks/useTextInput.ts @@ -0,0 +1,21 @@ +import debounce from 'lodash-es/debounce'; +import * as React from 'react'; +import { RecoilState, useRecoilState } from 'recoil'; + +const { useCallback, useState, useMemo } = React; + +export function useTextInut( + x: RecoilState +): [(e: React.ChangeEvent) => void, string] { + const [, setTextGlobal] = useRecoilState(x); + const [text, setText] = useState(''); + const setTextDebounced = useMemo(() => debounce(setTextGlobal, 300), [setTextGlobal]); + const onChange = useCallback( + (e: React.ChangeEvent) => { + setText(e.target.value); + setTextDebounced(e.target.value); + }, + [setTextDebounced] + ); + return [onChange, text]; +} diff --git a/src/i18n/en.ts b/src/i18n/en.ts new file mode 100644 index 0000000..8126195 --- /dev/null +++ b/src/i18n/en.ts @@ -0,0 +1,60 @@ +export const data = { + Overview: 'Overview', + Proxies: 'Proxies', + Rules: 'Rules', + Conns: 'Conns', + Config: 'Config', + Logs: 'Logs', + Upload: 'Upload', + Download: 'Download', + 'Upload Total': 'Upload Total', + 'Download Total': 'Download Total', + 'Active Connections': 'Active Connections', + 'Pause Refresh': 'Pause Refresh', + 'Resume Refresh': 'Resume Refresh', + close_all_connections: 'Close All Connections', + Search: 'Search', + Up: 'Up', + Down: 'Down', + 'Test Latency': 'Test Latency', + settings: 'settings', + sort_in_grp: 'Sorting in group', + hide_unavail_proxies: 'Hide unavailable proxies', + auto_close_conns: 'Automatically close old connections', + order_natural: 'Original order in config file', + order_latency_asc: 'By latency from small to big', + order_latency_desc: 'By latency from big to small', + order_name_asc: 'By name alphabetically (A-Z)', + order_name_desc: 'By name alphabetically (Z-A)', + Connections: 'Connections', + Active: 'Active', + Closed: 'Closed', + switch_theme: 'Switch theme', + theme: 'theme', + about: 'about', + no_logs: 'No logs yet, hang tight...', + chart_style: 'Chart Style', + latency_test_url: 'Latency Test URL', + lang: 'Language', + update_all_rule_provider: 'Update all rule providers', + update_all_proxy_provider: 'Update all proxy providers', + reload_config_file: 'Reload config file', + update_geo_databases_file: 'Update GEO Databases ', + flush_fake_ip_pool: 'Flush fake-ip data', + enable_tun_device: 'Enable TUN Device', + allow_lan: 'Allow LAN', + tls_sniffing: 'Sniffer', + c_host: 'Host', + c_sni: 'Sniff Host', + c_process: 'Process', + c_dl: 'DL', + c_ul: 'UL', + c_dl_speed: 'DL Speed', + c_ul_speed: 'UP Speed', + c_chains: 'Chains', + c_rule: 'Rule', + c_time: 'Time', + c_source: 'Source', + c_destination_ip: 'Destination IP', + c_type: 'Type', +}; diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts new file mode 100644 index 0000000..3356b6e --- /dev/null +++ b/src/i18n/zh.ts @@ -0,0 +1,60 @@ +export const data = { + Overview: '概览', + Proxies: '代理', + Rules: '规则', + Conns: '连接', + Config: '配置', + Logs: '日志', + Upload: '上传', + Download: '下载', + 'Upload Total': '上传总量', + 'Download Total': '下载总量', + 'Active Connections': '活动连接', + 'Pause Refresh': '暂停刷新', + 'Resume Refresh': '继续刷新', + close_all_connections: '关闭所有连接', + Search: '查找', + Up: '上传', + Down: '下载', + 'Test Latency': '延迟测速', + settings: '设置', + sort_in_grp: '代理组条目排序', + hide_unavail_proxies: '隐藏不可用代理', + auto_close_conns: '切换代理时自动断开旧连接', + order_natural: '原 config 文件中的排序', + order_latency_asc: '按延迟从小到大', + order_latency_desc: '按延迟从大到小', + order_name_asc: '按名称字母排序 (A-Z)', + order_name_desc: '按名称字母排序 (Z-A)', + Connections: '连接', + Active: '活动', + Closed: '已断开', + switch_theme: '切换主题', + theme: '主题', + about: '关于', + no_logs: '暂无日志...', + chart_style: '流量图样式', + latency_test_url: '延迟测速 URL', + lang: '语言', + update_all_rule_provider: '更新所有 rule provider', + update_all_proxy_provider: '更新所有 proxy provider', + reload_config_file: '重载配置文件', + update_geo_databases_file: '更新 GEO 数据库文件', + flush_fake_ip_pool: '清空 FakeIP 数据库', + enable_tun_device: '开启 TUN 转发', + allow_lan: '允许局域网连接', + tls_sniffing: 'SNI 嗅探', + c_host: '域名', + c_sni: '嗅探域名', + c_process: '进程', + c_dl: '下载', + c_ul: '上传', + c_dl_speed: '下载速率', + c_ul_speed: '上传速率', + c_chains: '节点链', + c_rule: '规则', + c_time: '连接时间', + c_source: '来源', + c_destination_ip: '目标IP', + c_type: '类型', +}; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..34a8f17 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,76 @@ +import '~/styles/main.scss'; +import './misc/i18n'; + +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import Modal from 'react-modal'; + +import App from './App'; +import * as swRegistration from './swRegistration'; + +const rootEl = document.getElementById('app'); +const root = createRoot(rootEl); + +Modal.setAppElement(rootEl); + +root.render(); + +swRegistration.register(); + +// eslint-disable-next-line no-console +console.log('Checkout the repo: https://github.com/MetaCubeX/yacd'); +// eslint-disable-next-line no-console +console.log('Version:', __VERSION__); + +window.onload = function startup() { + const el = document.getElementById('app'); + el.addEventListener('touchstart', onTouchStart, { passive: true }); + el.addEventListener('touchmove', onTouchMove, false); + el.addEventListener('touchend', onTouchEnd, false); +}; + +const touchData = { touching: false, trace: [] }; + +function onTouchStart(evt) { + if (evt.touches.length !== 1) { + touchData.touching = false; + touchData.trace = []; + return; + } + touchData.touching = true; + touchData.trace = [{ x: evt.touches[0].screenX, y: evt.touches[0].screenY }]; +} + +function onTouchMove(evt) { + if (!touchData.touching) return; + touchData.trace.push({ + x: evt.touches[0].screenX, + y: evt.touches[0].screenY, + }); +} + +function onTouchEnd() { + if (!touchData.touching) return; + const trace = touchData.trace; + touchData.touching = false; + touchData.trace = []; + handleTouch(trace); //判断touch类型并调用适当回调 +} + +function handleTouch(trace) { + const tags = ['/', '/proxies', '/rules', '/connections', '/configs', '/logs']; + const start = trace[0]; + const end = trace[trace.length - 1]; + const tag = window.location.hash.slice(1); + const index = tags.indexOf(tag); + console.log(index, tag, tags.length); + if (index === 3) return; + if (end.x - start.x > 200 && index > 0) { + window.location.hash = tags[index - 1]; + } else if (end.x - start.x < -200 && index < tags.length - 1) { + window.location.hash = tags[index + 1]; + if (index === -1) { + window.location.hash = tags[index + 2]; + } + } +} diff --git a/src/misc/chart-lib.ts b/src/misc/chart-lib.ts new file mode 100644 index 0000000..9a2bf35 --- /dev/null +++ b/src/misc/chart-lib.ts @@ -0,0 +1,23 @@ +import { + CategoryScale, + Chart, + Filler, + Legend, + LinearScale, + LineController, + LineElement, + PointElement, +} from 'chart.js'; + +// see https://www.chartjs.org/docs/latest/getting-started/integration.html#bundlers-webpack-rollup-etc +Chart.register( + LineElement, + PointElement, + LineController, + CategoryScale, + LinearScale, + Filler, + Legend +); + +export { Chart }; diff --git a/src/misc/chart.ts b/src/misc/chart.ts new file mode 100644 index 0000000..56ffe87 --- /dev/null +++ b/src/misc/chart.ts @@ -0,0 +1,79 @@ +import { createAsset } from 'use-asset'; + +import prettyBytes from './pretty-bytes'; +export const chartJSResource = createAsset(() => { + return import('~/misc/chart-lib'); +}); + +export const commonDataSetProps = { borderWidth: 1, pointRadius: 0, tension: 0.2, fill: true }; + +export const commonChartOptions: import('chart.js').ChartOptions<'line'> = { + responsive: true, + maintainAspectRatio: true, + plugins: { + legend: { labels: { boxWidth: 20 } }, + }, + scales: { + x: { display: false, type: 'category' }, + y: { + type: 'linear', + display: true, + grid: { + display: true, + color: '#555', + drawTicks: false, + }, + border: { + dash: [3, 6], + }, + ticks: { + callback(value: number) { + return prettyBytes(value) + '/s '; + }, + }, + }, + }, +}; + +export const chartStyles = [ + { + down: { + backgroundColor: 'rgba(176, 209, 132, 0.8)', + borderColor: 'rgb(176, 209, 132)', + }, + up: { + backgroundColor: 'rgba(181, 220, 231, 0.8)', + borderColor: 'rgb(181, 220, 231)', + }, + }, + { + up: { + backgroundColor: 'rgb(98, 190, 100)', + borderColor: 'rgb(78,146,79)', + }, + down: { + backgroundColor: 'rgb(160, 230, 66)', + borderColor: 'rgb(110, 156, 44)', + }, + }, + { + up: { + backgroundColor: 'rgba(94, 175, 223, 0.3)', + borderColor: 'rgb(94, 175, 223)', + }, + down: { + backgroundColor: 'rgba(139, 227, 195, 0.3)', + borderColor: 'rgb(139, 227, 195)', + }, + }, + { + up: { + backgroundColor: 'rgba(242, 174, 62, 0.3)', + borderColor: 'rgb(242, 174, 62)', + }, + down: { + backgroundColor: 'rgba(69, 154, 248, 0.3)', + borderColor: 'rgb(69, 154, 248)', + }, + }, +]; diff --git a/src/misc/createResource.ts b/src/misc/createResource.ts new file mode 100644 index 0000000..9ff57e5 --- /dev/null +++ b/src/misc/createResource.ts @@ -0,0 +1,45 @@ +// from https://gist.github.com/ryanflorence/e10cc9dbc0e259759ec942ba82e5b57c +export function createResource(getPromise: (key: string) => Promise) { + let cache = {}; + const inflight = {}; + const errors = {}; + + function load(key = 'default') { + inflight[key] = getPromise(key) + .then((val) => { + delete inflight[key]; + cache[key] = val; + }) + .catch((error) => { + errors[key] = error; + }); + return inflight[key]; + } + + function preload(key = 'default') { + if (cache[key] !== undefined || inflight[key]) return; + load(key); + } + + function read(key = 'default') { + if (cache[key] !== undefined) { + return cache[key]; + } else if (errors[key]) { + throw errors[key]; + } else if (inflight[key]) { + throw inflight[key]; + } else { + throw load(key); + } + } + + function clear(key: 'default') { + if (key) { + delete cache[key]; + } else { + cache = {}; + } + } + + return { preload, read, clear }; +} diff --git a/src/misc/errors.ts b/src/misc/errors.ts new file mode 100644 index 0000000..1bc369a --- /dev/null +++ b/src/misc/errors.ts @@ -0,0 +1,21 @@ +export const DOES_NOT_SUPPORT_FETCH = 0; + +export const errors = { + [DOES_NOT_SUPPORT_FETCH]: { + message: 'Browser not supported!', + detail: 'This browser does not support "fetch", please choose another one.', + }, + default: { + message: 'Oops, something went wrong!', + }, +}; + +export type Err = { code: number }; + +export function deriveMessageFromError(err: Err) { + const { code } = err; + if (typeof code === 'number') { + return errors[code]; + } + return errors.default; +} diff --git a/src/misc/i18n.ts b/src/misc/i18n.ts new file mode 100644 index 0000000..bbae42b --- /dev/null +++ b/src/misc/i18n.ts @@ -0,0 +1,61 @@ +import i18next from 'i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import HttpBackend from 'i18next-http-backend'; +import { initReactI18next } from 'react-i18next'; + +const allLocales = { + zh: import('~/i18n/zh'), + en: import('~/i18n/en'), +}; + +type BackendRequestCallback = (err: null, result: { status: number; data: any }) => void; + +i18next + .use(HttpBackend) + .use(initReactI18next) + .use(LanguageDetector) + .init({ + debug: process.env.NODE_ENV === 'development', + // resources, + backend: { + loadPath: '/__{{lng}}/{{ns}}.json', + request: function ( + _options: any, + url: string, + _payload: any, + callback: BackendRequestCallback + ) { + let p: PromiseLike<{ data: any }>; + + switch (url) { + case '/__zh/translation.json': + case '/__zh-CN/translation.json': + p = allLocales.zh; + break; + case '/__en/translation.json': + p = allLocales.en; + break; + default: + p = allLocales.zh; + break; + } + + if (p) { + p.then((mod) => { + callback(null, { status: 200, data: mod.data }); + }); + } + }, + }, + supportedLngs: ['en', 'zh'], + fallbackLng: 'en', + interpolation: { + escapeValue: false, + }, + }); + +if (process.env.NODE_ENV === 'development') { + window.i18n = i18next; +} + +export default i18next; diff --git a/src/misc/keycode.ts b/src/misc/keycode.ts new file mode 100644 index 0000000..d1dd935 --- /dev/null +++ b/src/misc/keycode.ts @@ -0,0 +1,6 @@ +export const keyCodes = { + Right: 39, + Left: 37, + Enter: 13, + Space: 32, +}; diff --git a/src/misc/motion.ts b/src/misc/motion.ts new file mode 100644 index 0000000..7fac864 --- /dev/null +++ b/src/misc/motion.ts @@ -0,0 +1,3 @@ +import { createResource } from './createResource'; + +export const framerMotionResouce = createResource(() => import('framer-motion')); diff --git a/src/misc/pretty-bytes.ts b/src/misc/pretty-bytes.ts new file mode 100644 index 0000000..68b6776 --- /dev/null +++ b/src/misc/pretty-bytes.ts @@ -0,0 +1,13 @@ +// steal from https://github.com/sindresorhus/pretty-bytes/blob/master/index.js + +const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + +export default function prettyBytes(n: number) { + if (n < 1000) { + return n + ' B'; + } + const exponent = Math.min(Math.floor(Math.log10(n) / 3), UNITS.length - 1); + n = Number((n / Math.pow(1000, exponent)).toPrecision(3)); + const unit = UNITS[exponent]; + return n + ' ' + unit; +} diff --git a/src/misc/query.ts b/src/misc/query.ts new file mode 100644 index 0000000..4c2a89d --- /dev/null +++ b/src/misc/query.ts @@ -0,0 +1,11 @@ +import { QueryCache, QueryClient } from 'react-query'; + +const queryCache = new QueryCache(); +export const queryClient = new QueryClient({ + queryCache, + defaultOptions: { + queries: { + suspense: true, + }, + }, +}); diff --git a/src/misc/request-helper.ts b/src/misc/request-helper.ts new file mode 100644 index 0000000..db28cde --- /dev/null +++ b/src/misc/request-helper.ts @@ -0,0 +1,45 @@ +import { trimTrailingSlash } from '~/misc/utils'; +import { ClashAPIConfig, LogsAPIConfig } from '~/types'; + +const headersCommon = { 'Content-Type': 'application/json' }; + +function genCommonHeaders({ secret }: { secret?: string }) { + const h = { ...headersCommon }; + if (secret) { + h['Authorization'] = `Bearer ${secret}`; + } + return h; +} +function buildWebSocketURLBase(baseURL: string, params: URLSearchParams, endpoint: string) { + const qs = '?' + params.toString(); + const url = new URL(baseURL); + url.protocol === 'https:' ? (url.protocol = 'wss:') : (url.protocol = 'ws:'); + return `${trimTrailingSlash(url.href)}${endpoint}${qs}`; +} + +export function getURLAndInit({ baseURL, secret }: ClashAPIConfig) { + const headers = genCommonHeaders({ secret }); + return { + url: baseURL, + init: { headers }, + }; +} + +export function buildWebSocketURL(apiConfig: ClashAPIConfig, endpoint: string) { + const { baseURL, secret } = apiConfig; + const params = new URLSearchParams({ + token: secret, + }); + + return buildWebSocketURLBase(baseURL, params, endpoint); +} + +export function buildLogsWebSocketURL(apiConfig: LogsAPIConfig, endpoint: string) { + const { baseURL, secret, logLevel } = apiConfig; + const params = new URLSearchParams({ + token: secret, + level: logLevel, + }); + + return buildWebSocketURLBase(baseURL, params, endpoint); +} diff --git a/src/misc/shallowEqual.ts b/src/misc/shallowEqual.ts new file mode 100644 index 0000000..937bc27 --- /dev/null +++ b/src/misc/shallowEqual.ts @@ -0,0 +1,31 @@ +const hasOwn = Object.prototype.hasOwnProperty; + +function is(x, y) { + if (x === y) { + return x !== 0 || y !== 0 || 1 / x === 1 / y; + } else { + // eslint-disable-next-line no-self-compare + return x !== x && y !== y; + } +} + +export default function shallowEqual(objA, objB) { + if (is(objA, objB)) return true; + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + const keysA = Object.keys(objA); + const keysB = Object.keys(objB); + + if (keysA.length !== keysB.length) return false; + + for (let i = 0; i < keysA.length; i++) { + if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { + return false; + } + } + + return true; +} diff --git a/src/misc/storage.ts b/src/misc/storage.ts new file mode 100644 index 0000000..15d85d3 --- /dev/null +++ b/src/misc/storage.ts @@ -0,0 +1,32 @@ +// manage localStorage + +const StorageKey = 'yacd.metacubex.one'; + +function loadState() { + try { + const serialized = localStorage.getItem(StorageKey); + if (!serialized) return undefined; + return JSON.parse(serialized); + } catch (err) { + return undefined; + } +} + +function saveState(state) { + try { + const serialized = JSON.stringify(state); + localStorage.setItem(StorageKey, serialized); + } catch (err) { + // ignore + } +} + +function clearState() { + try { + localStorage.removeItem(StorageKey); + } catch (err) { + // ignore + } +} + +export { loadState, saveState, clearState }; diff --git a/src/misc/utils.ts b/src/misc/utils.ts new file mode 100644 index 0000000..9497026 --- /dev/null +++ b/src/misc/utils.ts @@ -0,0 +1,35 @@ +export function throttle(fn: (...args: T) => unknown, timeout: number) { + let pending = false; + + return (...args: T) => { + if (!pending) { + pending = true; + fn(...args); + setTimeout(() => { + pending = false; + }, timeout); + } + }; +} + +export function debounce(fn: (...args: T) => unknown, timeout: number) { + let timeoutId: ReturnType; + return (...args: T) => { + if (timeoutId) clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + fn(...args); + }, timeout); + }; +} + +export function trimTrailingSlash(s: string) { + return s.replace(/\/$/, ''); +} + +export function pad0(number: number | string, len: number): string { + let output = String(number); + while (output.length < len) { + output = '0' + output; + } + return output; +} diff --git a/src/store/app.ts b/src/store/app.ts new file mode 100644 index 0000000..f6110de --- /dev/null +++ b/src/store/app.ts @@ -0,0 +1,219 @@ +import { DispatchFn, GetStateFn, State, StateApp } from '~/store/types'; + +import { loadState, saveState } from '../misc/storage'; +import { debounce, trimTrailingSlash } from '../misc/utils'; +import { fetchConfigs } from './configs'; +import { closeModal } from './modals'; + +export const getClashAPIConfig = (s: State) => { + const idx = s.app.selectedClashAPIConfigIndex; + return s.app.clashAPIConfigs[idx]; +}; +export const getSelectedClashAPIConfigIndex = (s: State) => s.app.selectedClashAPIConfigIndex; +export const getClashAPIConfigs = (s: State) => s.app.clashAPIConfigs; +export const getTheme = (s: State) => s.app.theme; +export const getSelectedChartStyleIndex = (s: State) => s.app.selectedChartStyleIndex; +export const getLatencyTestUrl = (s: State) => s.app.latencyTestUrl; +export const getCollapsibleIsOpen = (s: State) => s.app.collapsibleIsOpen; +export const getProxySortBy = (s: State) => s.app.proxySortBy; +export const getHideUnavailableProxies = (s: State) => s.app.hideUnavailableProxies; +export const getAutoCloseOldConns = (s: State) => s.app.autoCloseOldConns; +export const getLogStreamingPaused = (s: State) => s.app.logStreamingPaused; + +const saveStateDebounced = debounce(saveState, 600); + +function findClashAPIConfigIndex(getState: GetStateFn, { baseURL, secret }) { + const arr = getClashAPIConfigs(getState()); + for (let i = 0; i < arr.length; i++) { + const x = arr[i]; + if (x.baseURL === baseURL && x.secret === secret) return i; + } +} + +export function addClashAPIConfig({ baseURL, secret }) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + // already exists + if (idx) return; + + const clashAPIConfig = { baseURL, secret, addedAt: Date.now() }; + dispatch('addClashAPIConfig', (s) => { + s.app.clashAPIConfigs.push(clashAPIConfig); + }); + // side effect + saveState(getState().app); + }; +} + +export function removeClashAPIConfig({ baseURL, secret }) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + dispatch('removeClashAPIConfig', (s) => { + s.app.clashAPIConfigs.splice(idx, 1); + }); + // side effect + saveState(getState().app); + }; +} + +export function selectClashAPIConfig({ baseURL, secret }) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const idx = findClashAPIConfigIndex(getState, { baseURL, secret }); + const curr = getSelectedClashAPIConfigIndex(getState()); + if (curr !== idx) { + dispatch('selectClashAPIConfig', (s) => { + s.app.selectedClashAPIConfigIndex = idx; + }); + } + // side effect + saveState(getState().app); + + // manual clean up is too complex + // we just reload the app + try { + window.location.reload(); + } catch (err) { + // ignore + } + }; +} + +// unused +export function updateClashAPIConfig({ baseURL, secret }) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const clashAPIConfig = { baseURL, secret }; + dispatch('appUpdateClashAPIConfig', (s) => { + s.app.clashAPIConfigs[0] = clashAPIConfig; + }); + // side effect + saveState(getState().app); + dispatch(closeModal('apiConfig')); + dispatch(fetchConfigs(clashAPIConfig)); + }; +} + +const rootEl = document.querySelector('html'); +type ThemeType = 'dark' | 'light' | 'auto'; + +function setTheme(theme: ThemeType = 'dark') { + if (theme === 'auto') { + rootEl.setAttribute('data-theme', 'auto'); + } else if (theme === 'dark') { + rootEl.setAttribute('data-theme', 'dark'); + } else { + rootEl.setAttribute('data-theme', 'light'); + } +} + +export function switchTheme(nextTheme = 'auto') { + return (dispatch: DispatchFn, getState: GetStateFn) => { + const currentTheme = getTheme(getState()); + if (currentTheme === nextTheme) return; + // side effect + setTheme(nextTheme as ThemeType); + dispatch('storeSwitchTheme', (s) => { + s.app.theme = nextTheme; + }); + // side effect + saveState(getState().app); + }; +} + +export function selectChartStyleIndex(selectedChartStyleIndex: number | string) { + return (dispatch: DispatchFn, getState: GetStateFn) => { + dispatch('appSelectChartStyleIndex', (s) => { + s.app.selectedChartStyleIndex = Number(selectedChartStyleIndex); + }); + // side effect + saveState(getState().app); + }; +} + +export function updateAppConfig(name: string, value: unknown) { + return (dispatch: DispatchFn, getState: GetStateFn) => { + dispatch('appUpdateAppConfig', (s) => { + s.app[name] = value; + }); + // side effect + saveState(getState().app); + }; +} + +export function updateCollapsibleIsOpen(prefix: string, name: string, v: boolean) { + return (dispatch: DispatchFn, getState: GetStateFn) => { + dispatch('updateCollapsibleIsOpen', (s: State) => { + s.app.collapsibleIsOpen[`${prefix}:${name}`] = v; + }); + // side effect + saveStateDebounced(getState().app); + }; +} + +const defaultClashAPIConfig = { + baseURL: document.getElementById('app')?.getAttribute('data-base-url') ?? 'http://127.0.0.1:9090', + secret: '', + addedAt: 0, +}; +// type Theme = 'light' | 'dark'; +const defaultState: StateApp = { + selectedClashAPIConfigIndex: 0, + clashAPIConfigs: [defaultClashAPIConfig], + + latencyTestUrl: 'http://www.gstatic.com/generate_204', + selectedChartStyleIndex: 0, + theme: 'dark', + + // type { [string]: boolean } + collapsibleIsOpen: {}, + // how proxies are sorted in a group or provider + proxySortBy: 'Natural', + hideUnavailableProxies: false, + autoCloseOldConns: false, + logStreamingPaused: false, +}; + +function parseConfigQueryString() { + const { search } = window.location; + const collector: Record = {}; + if (typeof search !== 'string' || search === '') return collector; + const qs = search.replace(/^\?/, '').split('&'); + for (let i = 0; i < qs.length; i++) { + const [k, v] = qs[i].split('='); + collector[k] = encodeURIComponent(v); + } + return collector; +} + +export function initialState() { + let s = loadState(); + s = { ...defaultState, ...s }; + const query = parseConfigQueryString(); + + const conf = s.clashAPIConfigs[s.selectedClashAPIConfigIndex]; + if (conf) { + const url = new URL(conf.baseURL); + if (query.hostname) { + if (query.hostname.indexOf('http') === 0) { + url.href = decodeURIComponent(query.hostname); + } else { + url.hostname = query.hostname; + } + } + if (query.port) { + url.port = query.port; + } + // url.href is a stringifier and it appends a trailing slash + // that is not we want + conf.baseURL = trimTrailingSlash(url.href); + if (query.secret) { + conf.secret = query.secret; + } + } + + if (query.theme === 'dark' || query.theme === 'light') { + s.theme = query.theme; + } + // set initial theme + setTheme(s.theme); + return s; +} diff --git a/src/store/configs.ts b/src/store/configs.ts new file mode 100644 index 0000000..1d697dd --- /dev/null +++ b/src/store/configs.ts @@ -0,0 +1,185 @@ +import { + ClashGeneralConfig, + DispatchFn, + GetStateFn, + State, + StateConfigs, + TunPartial, +} from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +import * as configsAPI from '../api/configs'; +import * as trafficAPI from '../api/traffic'; +import { openModal } from './modals'; + +export const getConfigs = (s: State) => s.configs.configs; +export const getHaveFetched = (s: State) => s.configs.haveFetchedConfig; +export const getLogLevel = (s: State) => s.configs.configs['log-level']; + +export function fetchConfigs(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + let res: Response; + try { + res = await configsAPI.fetchConfigs(apiConfig); + } catch (err) { + // TypeError and AbortError + dispatch(openModal('apiConfig')); + return; + } + + if (!res.ok) { + console.log('Error fetch configs', res.statusText); + dispatch(openModal('apiConfig')); + return; + } + + const payload = await res.json(); + + dispatch('store/configs#fetchConfigs', (s) => { + s.configs.configs = payload; + }); + + const haveFetchedConfig = getHaveFetched(getState()); + + if (haveFetchedConfig) { + // normally user will land on the "traffic chart" page first + // calling this here will let the data start streaming + // the traffic chart should already subscribed to the streaming + trafficAPI.fetchData(apiConfig); + } else { + dispatch(markHaveFetchedConfig()); + } + }; +} + +function markHaveFetchedConfig() { + return (dispatch: DispatchFn) => { + dispatch('store/configs#markHaveFetchedConfig', (s: State) => { + s.configs.haveFetchedConfig = true; + }); + }; +} + +type generalConfig = Omit; + +export function updateConfigs( + apiConfig: ClashAPIConfig, + partialConfg: TunPartial +) { + return async (dispatch: DispatchFn) => { + configsAPI + .updateConfigs(apiConfig, partialConfg) + .then( + (res) => { + if (res.ok === false) { + // eslint-disable-next-line no-console + console.log('Error update configs', res.statusText); + } + }, + (err) => { + // eslint-disable-next-line no-console + console.log('Error update configs', err); + throw err; + } + ) + .then(() => { + dispatch(fetchConfigs(apiConfig)); + }); + + dispatch('storeConfigsOptimisticUpdateConfigs', (s) => { + s.configs.configs = { ...s.configs.configs, ...partialConfg } as generalConfig; + }); + }; +} + +export function reloadConfigFile(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn) => { + configsAPI + .reloadConfigFile(apiConfig) + .then( + (res) => { + if (res.ok === false) { + // eslint-disable-next-line no-console + console.log('Error reload config file', res.statusText); + } + }, + (err) => { + // eslint-disable-next-line no-console + console.log('Error reload config file', err); + throw err; + } + ) + .then(() => { + dispatch(fetchConfigs(apiConfig)); + }); + }; +} + +export function updateGeoDatabasesFile(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn) => { + configsAPI + .updateGeoDatabasesFile(apiConfig) + .then( + (res) => { + if (res.ok === false) { + // eslint-disable-next-line no-console + console.log('Error update geo databases file', res.statusText); + } + }, + (err) => { + // eslint-disable-next-line no-console + console.log('Error update geo databases file', err); + throw err; + } + ) + .then(() => { + dispatch(fetchConfigs(apiConfig)); + }); + }; +} + +export function flushFakeIPPool(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn) => { + configsAPI + .flushFakeIPPool(apiConfig) + .then( + (res) => { + if (res.ok === false) { + // eslint-disable-next-line no-console + console.log('Error flush FakeIP pool', res.statusText); + } + }, + (err) => { + // eslint-disable-next-line no-console + console.log('Error flush FakeIP pool', err); + throw err; + } + ) + .then(() => { + dispatch(fetchConfigs(apiConfig)); + }); + }; +} + +export const initialState: StateConfigs = { + configs: { + port: 7890, + 'socks-port': 7891, + 'mixed-port': 0, + 'redir-port': 0, + 'tproxy-port': 0, + 'mitm-port': 0, + 'allow-lan': false, + mode: 'rule', + 'log-level': 'uninit', + sniffing: false, + tun: { + enable: false, + device: '', + stack: '', + 'dns-hijack': [], + 'auto-route': false, + }, + }, + haveFetchedConfig: false, +}; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..4fc8e4c --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,33 @@ +import { + initialState as app, + removeClashAPIConfig, + selectChartStyleIndex, + selectClashAPIConfig, + updateAppConfig, + updateCollapsibleIsOpen, +} from './app'; +import { initialState as configs } from './configs'; +import { initialState as logs } from './logs'; +import { initialState as modals } from './modals'; +import { actions as proxiesActions, initialState as proxies } from './proxies'; + +export const initialState = { + app: app(), + modals, + configs, + proxies, + logs, +}; + +export const actions = { + selectChartStyleIndex, + updateAppConfig, + + app: { + updateCollapsibleIsOpen, + updateAppConfig, + removeClashAPIConfig, + selectClashAPIConfig, + }, + proxies: proxiesActions, +}; diff --git a/src/store/logs.ts b/src/store/logs.ts new file mode 100644 index 0000000..f5711ef --- /dev/null +++ b/src/store/logs.ts @@ -0,0 +1,58 @@ +import { createSelector } from 'reselect'; + +import { DispatchFn, GetStateFn, Log, State } from '~/store/types'; + +const LogSize = 300; + +const getLogs = (s: State) => s.logs.logs; +const getTail = (s: State) => s.logs.tail; +export const getSearchText = (s: State) => s.logs.searchText; +export const getLogsForDisplay = createSelector( + getLogs, + getTail, + getSearchText, + (logs, tail, searchText) => { + const x = []; + for (let i = tail; i >= 0; i--) { + x.push(logs[i]); + } + if (logs.length === LogSize) { + for (let i = LogSize - 1; i > tail; i--) { + x.push(logs[i]); + } + } + + if (searchText === '') return x; + return x.filter((r) => r.payload.toLowerCase().indexOf(searchText) >= 0); + } +); + +export function updateSearchText(text: string) { + return (dispatch: DispatchFn) => { + dispatch('logsUpdateSearchText', (s) => { + s.logs.searchText = text.toLowerCase(); + }); + }; +} + +export function appendLog(log: Log) { + return (dispatch: DispatchFn, getState: GetStateFn) => { + const s = getState(); + const logs = getLogs(s); + const tailCurr = getTail(s); + const tail = tailCurr >= LogSize - 1 ? 0 : tailCurr + 1; + // mutate intentionally for performance + logs[tail] = log; + + dispatch('logsAppendLog', (s: State) => { + s.logs.tail = tail; + }); + }; +} + +export const initialState = { + searchText: '', + logs: [], + // tail's initial value must be -1 + tail: -1, +}; diff --git a/src/store/modals.ts b/src/store/modals.ts new file mode 100644 index 0000000..0b27ce9 --- /dev/null +++ b/src/store/modals.ts @@ -0,0 +1,19 @@ +import { DispatchFn } from './types'; + +export function openModal(modalName: string) { + return (dispatch: DispatchFn) => { + dispatch(`openModal:${modalName}`, (s) => { + s.modals[modalName] = true; + }); + }; +} + +export function closeModal(modalName: string) { + return (dispatch: DispatchFn) => { + dispatch(`closeModal:${modalName}`, (s) => { + s.modals[modalName] = false; + }); + }; +} + +export const initialState = { apiConfig: false }; diff --git a/src/store/proxies.tsx b/src/store/proxies.tsx new file mode 100644 index 0000000..1075d29 --- /dev/null +++ b/src/store/proxies.tsx @@ -0,0 +1,397 @@ +import { atom } from 'recoil'; + +/* import { ProxyItem, ProxiesMapping, DelayMapping } from '~/store/types'; */ +import { + DispatchFn, + FormattedProxyProvider, + GetStateFn, + ProxiesMapping, + ProxyItem, + ProxyProvider, + State, + StateProxies, + SwitchProxyCtxItem, +} from '~/store/types'; +import { ClashAPIConfig } from '~/types'; + +import * as connAPI from '../api/connections'; +import * as proxiesAPI from '../api/proxies'; +import { getAutoCloseOldConns, getLatencyTestUrl } from './app'; + +export const initialState: StateProxies = { + proxies: {}, + delay: {}, + groupNames: [], + showModalClosePrevConns: false, +}; + +const noop = () => null; + +// see all types: +// https://github.com/Dreamacro/clash/blob/master/constant/adapters.go + +// const ProxyTypeBuiltin = ['DIRECT', 'GLOBAL', 'REJECT']; +// const ProxyGroupTypes = ['Fallback', 'URLTest', 'Selector', 'LoadBalance']; +// const ProxyTypes = ['Shadowsocks', 'Snell', 'Socks5', 'Http', 'Vmess']; + +export const NonProxyTypes = [ + 'Direct', + 'Fallback', + 'Reject', + 'Pass', + 'Selector', + 'URLTest', + 'LoadBalance', + 'Unknown', +]; + +export const getProxies = (s: State) => s.proxies.proxies; +export const getDelay = (s: State) => s.proxies.delay; +export const getProxyGroupNames = (s: State) => s.proxies.groupNames; +export const getProxyProviders = (s: State) => s.proxies.proxyProviders || []; +export const getDangleProxyNames = (s: State) => s.proxies.dangleProxyNames; +export const getShowModalClosePrevConns = (s: State) => s.proxies.showModalClosePrevConns; + +export function fetchProxies(apiConfig: ClashAPIConfig) { + return async (dispatch: any, getState: any) => { + const [proxiesData, providersData] = await Promise.all([ + proxiesAPI.fetchProxies(apiConfig), + proxiesAPI.fetchProviderProxies(apiConfig), + ]); + + const { providers: proxyProviders, proxies: providerProxies } = formatProxyProviders( + providersData.providers + ); + const proxies = { ...providerProxies, ...proxiesData.proxies }; + const [groupNames, proxyNames] = retrieveGroupNamesFrom(proxies); + + const delayPrev = getDelay(getState()); + const delayNext = { ...delayPrev }; + + for (let i = 0; i < proxyNames.length; i++) { + const name = proxyNames[i]; + const { history } = proxies[name] || { history: [] }; + const h = history[history.length - 1]; + if (h && typeof h.delay === 'number') { + delayNext[name] = { number: h.delay }; + } + } + + // proxies that are not from a provider + const dangleProxyNames = []; + for (const v of proxyNames) { + if (!providerProxies[v]) dangleProxyNames.push(v); + } + + dispatch('store/proxies#fetchProxies', (s: State) => { + s.proxies.proxies = proxies; + s.proxies.groupNames = groupNames; + s.proxies.delay = delayNext; + s.proxies.proxyProviders = proxyProviders; + s.proxies.dangleProxyNames = dangleProxyNames; + }); + }; +} + +export function updateProviderByName(apiConfig: ClashAPIConfig, name: string) { + return async (dispatch: DispatchFn) => { + try { + await proxiesAPI.updateProviderByName(apiConfig, name); + } catch (x) { + // ignore + } + // should be optimized + // but ¯\_(ツ)_/¯ + dispatch(fetchProxies(apiConfig)); + }; +} + +export function updateProviders(apiConfig: ClashAPIConfig, names: string[]) { + return async (dispatch: DispatchFn) => { + for (let i = 0; i < names.length; i++) { + try { + await proxiesAPI.updateProviderByName(apiConfig, names[i]); + } catch (x) { + // ignore + } + } + // should be optimized + // but ¯\_(ツ)_/¯ + dispatch(fetchProxies(apiConfig)); + }; +} + +async function healthcheckProviderByNameInternal(apiConfig: ClashAPIConfig, name: string) { + try { + await proxiesAPI.healthcheckProviderByName(apiConfig, name); + } catch (x) { + // ignore + } +} + +export function healthcheckProviderByName(apiConfig: ClashAPIConfig, name: string) { + return async (dispatch: DispatchFn) => { + await healthcheckProviderByNameInternal(apiConfig, name); + // should be optimized + // but ¯\_(ツ)_/¯ + await dispatch(fetchProxies(apiConfig)); + }; +} + +async function closeGroupConns( + apiConfig: ClashAPIConfig, + groupName: string, + exceptionItemName: string +) { + const res = await connAPI.fetchConns(apiConfig); + if (!res.ok) { + console.log('unable to fetch all connections', res.statusText); + /* throw new Error(); */ + } + const json = await res.json(); + const connections = json.connections; + const idsToClose = []; + for (const conn of connections) { + if ( + // include the groupName + conn.chains.indexOf(groupName) > -1 && + // but not include the itemName + conn.chains.indexOf(exceptionItemName) < 0 + ) { + idsToClose.push(conn.id); + } + } + + await Promise.all(idsToClose.map((id) => connAPI.closeConnById(apiConfig, id).catch(noop))); +} + +function resolveChain(proxies: ProxiesMapping, groupName: string, itemName: string) { + const chain = [itemName, groupName]; + + let child: ProxyItem; + let childKey = itemName; + while ((child = proxies[childKey]) && child.now) { + chain.unshift(child.now); + childKey = child.now; + } + return chain; +} + +async function switchProxyImpl( + dispatch: DispatchFn, + getState: GetStateFn, + apiConfig: ClashAPIConfig, + groupName: string, + itemName: string +) { + try { + const res = await proxiesAPI.requestToSwitchProxy(apiConfig, groupName, itemName); + if (res.ok === false) { + throw new Error(`failed to switch proxy: res.statusText`); + } + } catch (err) { + // eslint-disable-next-line no-console + console.log(err, 'failed to swith proxy'); + throw err; + } + + dispatch(fetchProxies(apiConfig)); + const autoCloseOldConns = getAutoCloseOldConns(getState()); + if (autoCloseOldConns) { + // use fresh state + const proxies = getProxies(getState()); + // no wait + closePrevConns(apiConfig, proxies, { groupName, itemName }); + } + + /* dispatch('showModalClosePrevConns', (s: GlobalState) => { */ + /* s.proxies.showModalClosePrevConns = true; */ + /* s.proxies.switchProxyCtx = { to: { groupName, itemName } }; */ + /* }); */ +} + +function closeModalClosePrevConns() { + return (dispatch: DispatchFn) => { + dispatch('closeModalClosePrevConns', (s: State) => { + s.proxies.showModalClosePrevConns = false; + }); + }; +} + +function closePrevConns( + apiConfig: ClashAPIConfig, + proxies: ProxiesMapping, + switchTo: SwitchProxyCtxItem +) { + // we must have fetched the proxies before + // so the proxies here is fresh + /* const proxies = s.proxies.proxies; */ + const chain = resolveChain(proxies, switchTo.groupName, switchTo.itemName); + closeGroupConns(apiConfig, switchTo.groupName, chain[0]); +} + +function closePrevConnsAndTheModal(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const s = getState(); + const switchTo = s.proxies.switchProxyCtx?.to; + if (!switchTo) { + dispatch(closeModalClosePrevConns()); + return; + } + + // we must have fetched the proxies before + // so the proxies here is fresh + const proxies = s.proxies.proxies; + closePrevConns(apiConfig, proxies, switchTo); + + dispatch('closePrevConnsAndTheModal', (s: State) => { + s.proxies.showModalClosePrevConns = false; + s.proxies.switchProxyCtx = undefined; + }); + }; +} + +export function switchProxy(apiConfig: ClashAPIConfig, groupName: string, itemName: string) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + // switch proxy asynchronously + switchProxyImpl(dispatch, getState, apiConfig, groupName, itemName).catch(noop); + + // optimistic UI update + dispatch('store/proxies#switchProxy', (s) => { + const proxies = s.proxies.proxies; + if (proxies[groupName] && proxies[groupName].now) { + proxies[groupName].now = itemName; + } + }); + }; +} + +function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const latencyTestUrl = getLatencyTestUrl(getState()); + const res = await proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl); + let error = ''; + if (res.ok === false) { + error = res.statusText; + } + const { delay } = await res.json(); + + const delayPrev = getDelay(getState()); + const delayNext = { + ...delayPrev, + [name]: { + error, + number: delay, + }, + }; + + dispatch('requestDelayForProxyOnce', (s) => { + s.proxies.delay = delayNext; + }); + }; +} + +export function requestDelayForProxy(apiConfig: ClashAPIConfig, name: string) { + return async (dispatch: DispatchFn) => { + await dispatch(requestDelayForProxyOnce(apiConfig, name)); + }; +} + +export function requestDelayForProxies(apiConfig: ClashAPIConfig, names: string[]) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const proxyNames = getDangleProxyNames(getState()); + + const works = names + // remove names that are provided by proxy providers + .filter((p) => proxyNames.indexOf(p) > -1) + .map((p) => dispatch(requestDelayForProxy(apiConfig, p))); + await Promise.all(works); + await dispatch(fetchProxies(apiConfig)); + }; +} + +export function requestDelayAll(apiConfig: ClashAPIConfig) { + return async (dispatch: DispatchFn, getState: GetStateFn) => { + const proxyNames = getDangleProxyNames(getState()); + await Promise.all(proxyNames.map((p) => dispatch(requestDelayForProxy(apiConfig, p)))); + const proxyProviders = getProxyProviders(getState()); + // one by one + for (const p of proxyProviders) { + await healthcheckProviderByNameInternal(apiConfig, p.name); + } + await dispatch(fetchProxies(apiConfig)); + }; +} + +function retrieveGroupNamesFrom(proxies: Record) { + let groupNames = []; + let globalAll: string[]; + const proxyNames = []; + for (const prop in proxies) { + const p = proxies[prop]; + if (p.all && Array.isArray(p.all)) { + groupNames.push(prop); + if (prop === 'GLOBAL') { + globalAll = p.all; + } + } else if (NonProxyTypes.indexOf(p.type) < 0) { + proxyNames.push(prop); + } + } + if (globalAll) { + // Put GLOBAL in the end + globalAll.push('GLOBAL'); + // Sort groups according to its index in GLOBAL group + groupNames = groupNames + .map((name) => [globalAll.indexOf(name), name]) + .sort((a, b) => a[0] - b[0]) + .map((group) => group[1]); + } + return [groupNames, proxyNames]; +} + +type ProvidersRaw = { + [key: string]: ProxyProvider; +}; + +function formatProxyProviders(providersInput: ProvidersRaw): { + providers: Array; + proxies: { [key: string]: ProxyItem }; +} { + const keys = Object.keys(providersInput); + const providers = []; + const proxies = {}; + for (let i = 0; i < keys.length; i++) { + const provider: ProxyProvider = providersInput[keys[i]]; + if (provider.name === 'default' || provider.vehicleType === 'Compatible') { + continue; + } + const proxiesArr = provider.proxies; + const names = []; + for (let j = 0; j < proxiesArr.length; j++) { + const proxy = proxiesArr[j]; + proxies[proxy.name] = proxy; + names.push(proxy.name); + } + + // mutate directly + provider.proxies = names; + providers.push(provider); + } + + return { + providers, + proxies, + }; +} + +export const actions = { + requestDelayForProxies, + closeModalClosePrevConns, + closePrevConnsAndTheModal, +}; + +export const proxyFilterText = atom({ + key: 'proxyFilterText', + default: '', +}); diff --git a/src/store/rules.ts b/src/store/rules.ts new file mode 100644 index 0000000..bdd835d --- /dev/null +++ b/src/store/rules.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const ruleFilterText = atom({ + key: 'ruleFilterText', + default: '', +}); diff --git a/src/store/types.ts b/src/store/types.ts new file mode 100644 index 0000000..86a3df3 --- /dev/null +++ b/src/store/types.ts @@ -0,0 +1,144 @@ +import type { ClashAPIConfig } from '~/types'; + +export type ClashAPIConfigWithAddedAt = ClashAPIConfig & { addedAt?: number }; +export type StateApp = { + selectedClashAPIConfigIndex: number; + clashAPIConfigs: ClashAPIConfigWithAddedAt[]; + + latencyTestUrl: string; + selectedChartStyleIndex: number; + theme: string; + + collapsibleIsOpen: Record; + proxySortBy: string; + hideUnavailableProxies: boolean; + autoCloseOldConns: boolean; + logStreamingPaused: boolean; +}; + +export type ClashTunConfig = { + enable: boolean; + device?: string; + stack: string; + 'dns-hijack': string[]; + 'auto-route': boolean; +}; + +export type ClashGeneralConfig = { + port: number; + 'socks-port': number; + 'mixed-port': number; + 'redir-port': number; + 'tproxy-port': number; + 'mitm-port'?: number; + 'allow-lan': boolean; + 'interface-name'?: string; + mode: string; + 'log-level': string; + sniffing?: boolean; + tun?: ClashTunConfig; +}; + +export type TunPartial = { + [P in keyof T]?: T[P] extends ClashTunConfig ? TunPartial : T[P]; +}; + +///// store.proxies + +type LatencyHistory = Array<{ time: string; delay: number }>; +type PrimitiveProxyType = 'Shadowsocks' | 'Snell' | 'Socks5' | 'Http' | 'Vmess'; + +export type SubscriptionInfo = { + Download?: number; + Upload?: number; + Total?: number; + Expire?: number; +}; +export type ProxyItem = { + name: string; + type: PrimitiveProxyType; + udp: boolean; + xudp?: boolean; + tfo: boolean; + history: LatencyHistory; + all?: string[]; + now?: string; +}; +export type ProxiesMapping = Record; +export type DelayMapping = Record; + +export type ProxyProvider = { + name: string; + type: 'Proxy'; + updatedAt: string; + vehicleType: 'HTTP' | 'File' | 'Compatible'; + proxies: Array; + subscriptionInfo?: SubscriptionInfo; +}; + +export type FormattedProxyProvider = Omit & { + proxies: string[]; +}; + +export type SwitchProxyCtxItem = { groupName: string; itemName: string }; +type SwitchProxyCtx = { + to: SwitchProxyCtxItem; +}; +export type StateProxies = { + proxies: ProxiesMapping; + delay: DelayMapping; + groupNames: string[]; + proxyProviders?: FormattedProxyProvider[]; + dangleProxyNames?: string[]; + + showModalClosePrevConns: boolean; + switchProxyCtx?: SwitchProxyCtx; +}; + +///// store.logs + +export type Log = { + time: string; + even: boolean; + payload: string; + type: string; + id: string; +}; + +export type StateLogs = { + searchText: string; + logs: Log[]; + tail: number; +}; + +///// store.configs + +export type StateConfigs = { + configs: ClashGeneralConfig; + haveFetchedConfig: boolean; +}; + +///// store.modals + +export type StateModals = { + apiConfig: boolean; +}; + +////// + +export type State = { + app: StateApp; + configs: StateConfigs; + proxies: StateProxies; + logs: StateLogs; + modals: StateModals; +}; + +export type GetStateFn = () => State; +export interface DispatchFn { + (msg: string, change: (s: State) => void): void; + (action: (dispatch: DispatchFn, getState: GetStateFn) => Promise): ReturnType< + typeof action + >; + (action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType; +} diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000..c447ce5 --- /dev/null +++ b/src/styles/main.scss @@ -0,0 +1,219 @@ +@import '@fontsource/inter/latin-400.css'; +@import '@fontsource/inter/latin-800.css'; +@import '@fontsource/roboto-mono/latin-400.css'; +@import 'modern-normalize/modern-normalize.css'; + +.relative { + position: relative; +} + +.border-left, +.border-top, +.border-bottom { + position: relative; +} + +%border { + position: absolute; + content: ''; + height: 1px; + width: 100%; + transform: scaleY(0.5) translateZ(0); + left: 0; + right: 0; + background: #555; +} + +%border1 { + position: absolute; + content: ''; + height: 100%; + width: 1px; + transform: scaleX(0.5) translateZ(0); + top: 0; + bottom: 0; + background: #555; +} + +.border-top::before { + @extend %border; + top: 0; +} + +.border-bottom::after { + @extend %border; + bottom: 0; +} + +.border-left::before { + @extend %border1; + left: 0; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +:root { + --font-mono: 'Roboto Mono', Menlo, monospace; + // prettier-ignore + --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, "Twemoji Mozilla", Segoe UI Emoji, Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '\5fae\8f6f\96c5\9ed1', Arial; + --color-focus-blue: #1a73e8; + --btn-bg: #387cec; +} + +body { + font-family: var(--font-normal); + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + ::-webkit-scrollbar { + z-index: 11; + background: transparent; + + &-thumb { + border-radius: 5px; + background: #3b5f76; + } + } + + ::-webkit-scrollbar:vertical { + width: 6px; + } + + ::-webkit-scrollbar:horizontal { + height: 6px; + } + + margin: 0; + padding: 0; +} + +@mixin dark { + --color-background: #202020; + --color-background2: rgba(32, 32, 32, 0.3); + --color-bg-card: #2d2d2d; + --card-hover-border-lightness: 30%; + --color-text: #ddd; + --color-text-secondary: #ccc; + --color-text-highlight: #fff; + --color-bg-sidebar: #2d2d30; + --color-sb-active-row-bg: #494b4e; + --color-input-bg: #2d2d30; + --color-input-border: #3f3f3f; + --color-toggle-bg: #353535; + --color-toggle-selected: #181818; + --color-icon: #c7c7c7; + --color-separator: #333; + --color-btn-bg: #232323; + --color-btn-fg: #bebebe; + --color-bg-proxy: #303030; + --color-row-odd: #282828; + --bg-log-info-tag: #454545; + --bg-modal: #1f1f20; + --bg-near-transparent: rgba(255, 255, 255, 0.1); + --bg-tooltip: #111; + --bc-tooltip: #555; + --select-border-color: #040404; + --select-bg-hover: url(data:image/svg+xml,%0A%20%20%20%20%3Csvg%20width%3D%228%22%20height%3D%2224%22%20viewBox%3D%220%200%208%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%207L7%2011H1L4%207Z%22%20fill%3D%22%23ffffff%22%20%2F%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%2017L1%2013L7%2013L4%2017Z%22%20fill%3D%22%23ffffff%22%20%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E%0A%20%20); + --bg-log-info-card: #262626; +} + +@mixin light { + --color-background: #eee; + --color-background2: rgba(240, 240, 240, 0.3); + --color-bg-card: #fafafa; + --card-hover-border-lightness: 80%; + --color-text: #222; + --color-text-secondary: #646464; + --color-text-highlight: #040404; + --color-bg-sidebar: #f8f8f8; + --color-sb-active-row-bg: #d8d8d8; + --color-input-bg: #f0f0f0; + --color-input-border: #c0c0c0; + --color-toggle-bg: #ffffff; + --color-toggle-selected: #d7d7d7; + --color-icon: #5b5b5b; + --color-separator: #ccc; + --color-btn-bg: #f4f4f4; + --color-btn-fg: #101010; + --color-bg-proxy: #fafafa; + --color-row-odd: #f5f5f5; + --bg-log-info-tag: #888; + --bg-modal: #fbfbfb; + --bg-near-transparent: rgba(0, 0, 0, 0.1); + --bg-tooltip: #f0f0f0; + --bc-tooltip: #ccc; + --select-border-color: #999999; + --select-bg-hover: url(data:image/svg+xml,%0A%20%20%20%20%3Csvg%20width%3D%228%22%20height%3D%2224%22%20viewBox%3D%220%200%208%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%207L7%2011H1L4%207Z%22%20fill%3D%22%23222222%22%20%2F%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%2017L1%2013L7%2013L4%2017Z%22%20fill%3D%22%23222222%22%20%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E%0A%20%20); + --bg-log-info-card: #f5f5f5; +} + +:root[data-theme='auto'] { + @media (prefers-color-scheme: dark) { + @include dark; + color-scheme: dark; + } + + @media (prefers-color-scheme: light) { + @include light; + color-scheme: light; + } +} + +:root[data-theme='dark'] { + @include dark; + color-scheme: dark; +} + +:root[data-theme='light'] { + @include light; + color-scheme: light; +} + +.flexCenter { + display: flex; + align-items: center; + justify-content: center; +} + +.fabgrp { + position: fixed; + z-index: 3; + right: 20px; + bottom: 20px; +} + +.visually-hidden { + position: absolute; + overflow: hidden; + clip: rect(0 0 0 0); + width: 1px; + height: 1px; + margin: -1px; + border: 0; + padding: 0; +} + +/**** @reach/tooltip/styles.css ****/ +:root { + --reach-tooltip: 1; +} + +[data-reach-tooltip] { + z-index: 1; + pointer-events: none; + position: absolute; + padding: 0.25em 0.5em; + box-shadow: 2px 2px 10px hsla(0, 0%, 0%, 0.1); + white-space: nowrap; + font-size: 85%; + background: var(--bg-tooltip); + color: var(--color-text); + border: solid 1px var(--bc-tooltip); + border-radius: 4px; +} diff --git a/src/styles/utils/custom-media.scss b/src/styles/utils/custom-media.scss new file mode 100644 index 0000000..f53ece0 --- /dev/null +++ b/src/styles/utils/custom-media.scss @@ -0,0 +1,3 @@ +@custom-media --breakpoint-not-small screen and (min-width: 30em); +@custom-media --breakpoint-medium screen and (min-width: 30em) and (max-width: 60em); +@custom-media --breakpoint-large screen and (min-width: 60em); diff --git a/src/sw.ts b/src/sw.ts new file mode 100644 index 0000000..f577677 --- /dev/null +++ b/src/sw.ts @@ -0,0 +1,74 @@ +/// +/* eslint-disable no-restricted-globals */ + +// This service worker can be customized! +// See https://developers.google.com/web/tools/workbox/modules +// for the list of available Workbox modules, or add any other +// code you'd like. +// You can also remove this file if you'd prefer not to use a +// service worker, and the Workbox build step will be skipped. + +import { clientsClaim } from 'workbox-core'; +import { ExpirationPlugin } from 'workbox-expiration'; +import { createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching'; +import { registerRoute } from 'workbox-routing'; +import { StaleWhileRevalidate } from 'workbox-strategies'; + +declare const self: ServiceWorkerGlobalScope; + +clientsClaim(); + +precacheAndRoute(self.__WB_MANIFEST); + +// Set up App Shell-style routing, so that all navigation requests +// are fulfilled with your index.html shell. Learn more at +// https://developers.google.com/web/fundamentals/architecture/app-shell +const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); +registerRoute( + // Return false to exempt requests from being fulfilled by index.html. + ({ request, url }: { request: Request; url: URL }) => { + // If this isn't a navigation, skip. + if (request.mode !== 'navigate') { + return false; + } + + // If this is a URL that starts with /_, skip. + if (url.pathname.startsWith('/_')) { + return false; + } + + // If this looks like a URL for a resource, because it contains + // a file extension, skip. + if (url.pathname.match(fileExtensionRegexp)) { + return false; + } + + // Return true to signal that we want to use the handler. + return true; + }, + createHandlerBoundToURL('index.html') +); + +// An example runtime caching route for requests that aren't handled by the +// precache, in this case same-origin .png requests like those from in public/ +registerRoute( + // Add in any other file extensions or routing criteria as needed. + ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), + // Customize this strategy as needed, e.g., by changing to CacheFirst. + new StaleWhileRevalidate({ + cacheName: 'images', + plugins: [ + // Ensure that once this runtime cache reaches a maximum size the + // least-recently used images are removed. + new ExpirationPlugin({ maxEntries: 50 }), + ], + }) +); + +// This allows the web app to trigger skipWaiting via +// registration.waiting.postMessage({type: 'SKIP_WAITING'}) +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); diff --git a/src/swRegistration.ts b/src/swRegistration.ts new file mode 100644 index 0000000..0a684a8 --- /dev/null +++ b/src/swRegistration.ts @@ -0,0 +1,127 @@ +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +); + +type Config = { + onSuccess?: (registration: ServiceWorkerRegistration) => void; + onUpdate?: (registration: ServiceWorkerRegistration) => void; +}; + +export function register(config?: Config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/sw.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log('This web app is being served cache-first by a service worker'); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl: string, config?: Config) { + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://cra.link/PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch((error) => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl: string, config?: Config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { 'Service-Worker': 'script' }, + }) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then((registration) => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.'); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready + .then((registration) => { + registration.unregister(); + }) + .catch((error) => { + console.error(error.message); + }); + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..8446dfc --- /dev/null +++ b/src/types.ts @@ -0,0 +1,6 @@ +export type ClashAPIConfig = { + baseURL: string; + secret?: string; +}; + +export type LogsAPIConfig = ClashAPIConfig & { logLevel: string }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f838048 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "~/*": ["src/*"] + }, + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"], + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + }, + "include": ["src"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ef53dca --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,39 @@ +import react from '@vitejs/plugin-react'; +import * as path from 'path'; +import { defineConfig } from 'vite'; +import { VitePWA } from 'vite-plugin-pwa'; + +import pkg from './package.json'; + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => ({ + define: { + __VERSION__: JSON.stringify(pkg.version), + 'process.env.NODE_ENV': JSON.stringify(mode), + 'process.env.PUBLIC_URL': JSON.stringify('./'), + }, + base: './', + resolve: { + alias: { + '~': path.resolve(__dirname, './src'), + }, + }, + publicDir: 'assets', + build: { + // sourcemap: true, + // the default value is 'dist' + // which make more sense + // but change this may break other people's tools + outDir: 'public', + }, + plugins: [ + react(), + VitePWA({ + srcDir: 'src', + outDir: 'public', + filename: 'sw.ts', + strategies: 'injectManifest', + base: './', + }), + ], +}));