From 5d67c0c2b279dd9f9c4a99a5bbc6d0fdcb0ca22a Mon Sep 17 00:00:00 2001 From: Lucas Jensen Date: Wed, 1 May 2024 09:19:01 -0700 Subject: [PATCH] initial commit --- README.md | 0 client/.env.example | 5 + client/.eslintrc.cjs | 18 + client/.github/workflows/build.yml | 33 + client/.gitignore | 27 + client/.prettierignore | 4 + client/.prettierrc | 1 + client/README.md | 46 + client/index.html | 27 + client/package-lock.json | 3819 +++++++++++++++++ client/package.json | 44 + client/public/bg.jpeg | Bin 0 -> 139228 bytes client/public/favicon.ico | Bin 0 -> 15406 bytes client/src/App.css | 68 + client/src/App.tsx | 86 + client/src/Auth/Profile.tsx | 30 + client/src/Auth/User.tsx | 13 + client/src/BackgroundStyle.tsx | 31 + .../src/Buttons/DeleteButton/DeleteButton.css | 34 + .../src/Buttons/DeleteButton/DeleteButton.tsx | 40 + client/src/Buttons/EditButton/EditButton.css | 34 + client/src/Buttons/EditButton/EditButton.tsx | 26 + client/src/Buttons/Login.tsx | 42 + client/src/Buttons/Logout.tsx | 25 + client/src/Cld/CloudinaryConfig.ts | 9 + client/src/EditModals/EditModal.tsx | 33 + .../EmailSignupButton/EmailSignupButton.css | 12 + .../EmailSignupButton/EmailSignupButton.tsx | 19 + client/src/ErrorModal/ErrorModal.css | 8 + client/src/ErrorModal/ErrorModal.tsx | 31 + client/src/Footer/Footer.css | 16 + client/src/Footer/Footer.tsx | 56 + client/src/Forms/Bio/BioForm.tsx | 110 + .../Confirmation/ConfirmationModal.tsx | 32 + client/src/Forms/Contact/ContactForm.css | 13 + client/src/Forms/Contact/ContactForm.tsx | 105 + client/src/Forms/Event/EventForm.css | 5 + client/src/Forms/Event/EventForm.tsx | 246 ++ .../Forms/HeadshotUpload/HeadshotUpload.css | 8 + .../HeadshotUpload/HeadshotUploadForm.tsx | 123 + .../Forms/PosterUpload/PosterUploadForm.tsx | 117 + client/src/Group/Group.css | 13 + client/src/Group/Group.tsx | 79 + client/src/Musicians/Musician/Bio/Bio.tsx | 63 + .../Musicians/Musician/Headshot/Headshot.css | 0 .../Musicians/Musician/Headshot/Headshot.tsx | 64 + client/src/Musicians/Musician/Musician.css | 9 + client/src/Musicians/Musician/Musician.tsx | 68 + client/src/Musicians/Musicians.css | 10 + client/src/Musicians/Musicians.tsx | 47 + .../NavBar/AdminDropdown/AdminDropdown.css | 7 + .../NavBar/AdminDropdown/AdminDropdown.tsx | 49 + client/src/NavBar/NavBar.css | 36 + client/src/NavBar/NavBar.tsx | 91 + client/src/Series/Events.tsx/Event/Event.css | 5 + client/src/Series/Events.tsx/Event/Event.tsx | 70 + client/src/Series/Events.tsx/Events.css | 3 + client/src/Series/Events.tsx/Events.tsx | 23 + client/src/Series/Series/Series.tsx | 90 + client/src/Series/Series/SeriesPoster.tsx | 57 + client/src/Series/SeriesList.css | 19 + client/src/Series/SeriesList.tsx | 106 + client/src/api.tsx | 293 ++ client/src/index.css | 14 + client/src/main.tsx | 16 + client/src/vite-env.d.ts | 1 + client/tsconfig.json | 27 + client/tsconfig.node.json | 10 + client/vite.config.ts | 8 + server/.env.example | 15 + server/.github/workflows/build.yml | 36 + server/.gitignore | 176 + server/.python-version | 1 + server/README.md | 45 + server/app/__init__.py | 1 + server/app/admin/__init__.py | 3 + server/app/admin/contact.py | 17 + server/app/admin/images.py | 48 + server/app/admin/oauth_token.py | 27 + server/app/controllers/__init__.py | 3 + server/app/controllers/base_controller.py | 28 + server/app/controllers/controller.py | 99 + server/app/controllers/events.py | 106 + server/app/controllers/group.py | 35 + server/app/controllers/musicians.py | 89 + server/app/controllers/users.py | 70 + server/app/db/__init__.py | 9 + server/app/db/base_queries.py | 32 + server/app/db/conn.py | 31 + server/app/db/events.py | 140 + server/app/db/group.py | 36 + server/app/db/musicians.py | 29 + server/app/db/users.py | 42 + server/app/main.py | 56 + server/app/models/__init__.py | 0 server/app/models/contact.py | 7 + server/app/models/event.py | 36 + server/app/models/group.py | 10 + server/app/models/musician.py | 16 + server/app/models/tgd.py | 12 + server/app/models/user.py | 13 + server/app/routers/__init__.py | 0 server/app/routers/contact.py | 18 + server/app/routers/events.py | 56 + server/app/routers/group.py | 27 + server/app/routers/musicians.py | 49 + server/app/routers/users.py | 29 + server/app/scripts/DDL.sql | 62 + server/app/scripts/__init__.py | 0 server/app/scripts/run.py | 11 + server/app/scripts/run.sh | 3 + server/app/scripts/seed.py | 266 ++ server/app/scripts/version.py | 18 + server/poetry.lock | 1390 ++++++ server/pyproject.toml | 37 + server/tests/__init__.py | 0 server/tests/test_app.py | 9 + 117 files changed, 9917 insertions(+) create mode 100644 README.md create mode 100644 client/.env.example create mode 100644 client/.eslintrc.cjs create mode 100644 client/.github/workflows/build.yml create mode 100644 client/.gitignore create mode 100644 client/.prettierignore create mode 100644 client/.prettierrc create mode 100644 client/README.md create mode 100644 client/index.html create mode 100644 client/package-lock.json create mode 100644 client/package.json create mode 100644 client/public/bg.jpeg create mode 100644 client/public/favicon.ico create mode 100644 client/src/App.css create mode 100644 client/src/App.tsx create mode 100644 client/src/Auth/Profile.tsx create mode 100644 client/src/Auth/User.tsx create mode 100644 client/src/BackgroundStyle.tsx create mode 100644 client/src/Buttons/DeleteButton/DeleteButton.css create mode 100644 client/src/Buttons/DeleteButton/DeleteButton.tsx create mode 100644 client/src/Buttons/EditButton/EditButton.css create mode 100644 client/src/Buttons/EditButton/EditButton.tsx create mode 100644 client/src/Buttons/Login.tsx create mode 100644 client/src/Buttons/Logout.tsx create mode 100644 client/src/Cld/CloudinaryConfig.ts create mode 100644 client/src/EditModals/EditModal.tsx create mode 100644 client/src/EmailSignupButton/EmailSignupButton.css create mode 100644 client/src/EmailSignupButton/EmailSignupButton.tsx create mode 100644 client/src/ErrorModal/ErrorModal.css create mode 100644 client/src/ErrorModal/ErrorModal.tsx create mode 100644 client/src/Footer/Footer.css create mode 100644 client/src/Footer/Footer.tsx create mode 100644 client/src/Forms/Bio/BioForm.tsx create mode 100644 client/src/Forms/Contact/Confirmation/ConfirmationModal.tsx create mode 100644 client/src/Forms/Contact/ContactForm.css create mode 100644 client/src/Forms/Contact/ContactForm.tsx create mode 100644 client/src/Forms/Event/EventForm.css create mode 100644 client/src/Forms/Event/EventForm.tsx create mode 100644 client/src/Forms/HeadshotUpload/HeadshotUpload.css create mode 100644 client/src/Forms/HeadshotUpload/HeadshotUploadForm.tsx create mode 100644 client/src/Forms/PosterUpload/PosterUploadForm.tsx create mode 100644 client/src/Group/Group.css create mode 100644 client/src/Group/Group.tsx create mode 100644 client/src/Musicians/Musician/Bio/Bio.tsx create mode 100644 client/src/Musicians/Musician/Headshot/Headshot.css create mode 100644 client/src/Musicians/Musician/Headshot/Headshot.tsx create mode 100644 client/src/Musicians/Musician/Musician.css create mode 100644 client/src/Musicians/Musician/Musician.tsx create mode 100644 client/src/Musicians/Musicians.css create mode 100644 client/src/Musicians/Musicians.tsx create mode 100644 client/src/NavBar/AdminDropdown/AdminDropdown.css create mode 100644 client/src/NavBar/AdminDropdown/AdminDropdown.tsx create mode 100644 client/src/NavBar/NavBar.css create mode 100644 client/src/NavBar/NavBar.tsx create mode 100644 client/src/Series/Events.tsx/Event/Event.css create mode 100644 client/src/Series/Events.tsx/Event/Event.tsx create mode 100644 client/src/Series/Events.tsx/Events.css create mode 100644 client/src/Series/Events.tsx/Events.tsx create mode 100644 client/src/Series/Series/Series.tsx create mode 100644 client/src/Series/Series/SeriesPoster.tsx create mode 100644 client/src/Series/SeriesList.css create mode 100644 client/src/Series/SeriesList.tsx create mode 100644 client/src/api.tsx create mode 100644 client/src/index.css create mode 100644 client/src/main.tsx create mode 100644 client/src/vite-env.d.ts create mode 100644 client/tsconfig.json create mode 100644 client/tsconfig.node.json create mode 100644 client/vite.config.ts create mode 100644 server/.env.example create mode 100644 server/.github/workflows/build.yml create mode 100644 server/.gitignore create mode 100644 server/.python-version create mode 100644 server/README.md create mode 100644 server/app/__init__.py create mode 100644 server/app/admin/__init__.py create mode 100644 server/app/admin/contact.py create mode 100644 server/app/admin/images.py create mode 100644 server/app/admin/oauth_token.py create mode 100644 server/app/controllers/__init__.py create mode 100644 server/app/controllers/base_controller.py create mode 100644 server/app/controllers/controller.py create mode 100644 server/app/controllers/events.py create mode 100644 server/app/controllers/group.py create mode 100644 server/app/controllers/musicians.py create mode 100644 server/app/controllers/users.py create mode 100644 server/app/db/__init__.py create mode 100644 server/app/db/base_queries.py create mode 100644 server/app/db/conn.py create mode 100644 server/app/db/events.py create mode 100644 server/app/db/group.py create mode 100644 server/app/db/musicians.py create mode 100644 server/app/db/users.py create mode 100644 server/app/main.py create mode 100644 server/app/models/__init__.py create mode 100644 server/app/models/contact.py create mode 100644 server/app/models/event.py create mode 100644 server/app/models/group.py create mode 100644 server/app/models/musician.py create mode 100644 server/app/models/tgd.py create mode 100644 server/app/models/user.py create mode 100644 server/app/routers/__init__.py create mode 100644 server/app/routers/contact.py create mode 100644 server/app/routers/events.py create mode 100644 server/app/routers/group.py create mode 100644 server/app/routers/musicians.py create mode 100644 server/app/routers/users.py create mode 100644 server/app/scripts/DDL.sql create mode 100644 server/app/scripts/__init__.py create mode 100644 server/app/scripts/run.py create mode 100755 server/app/scripts/run.sh create mode 100644 server/app/scripts/seed.py create mode 100644 server/app/scripts/version.py create mode 100644 server/poetry.lock create mode 100644 server/pyproject.toml create mode 100644 server/tests/__init__.py create mode 100644 server/tests/test_app.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..f8e6c96 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,5 @@ +[api] +VITE_API_URL=http://localhost:8000/ + +[oauth2-google] +VITE_GOOGLE_CLIENT_ID=some-value.apps.googleusercontent.com \ No newline at end of file diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..6e8698b --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/client/.github/workflows/build.yml b/client/.github/workflows/build.yml new file mode 100644 index 0000000..347c28e --- /dev/null +++ b/client/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: build app + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [21.x] + + steps: + - uses: actions/checkout@v3 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Lint + run: | + npm install + npm run prettier:check + + - name: Build + run: | + npm install + npm run build diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..846d282 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.env +.info.txt \ No newline at end of file diff --git a/client/.prettierignore b/client/.prettierignore new file mode 100644 index 0000000..ab53970 --- /dev/null +++ b/client/.prettierignore @@ -0,0 +1,4 @@ +# Ignore artifacts: +build +coverage +dist \ No newline at end of file diff --git a/client/.prettierrc b/client/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/client/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..59d8d85 --- /dev/null +++ b/client/README.md @@ -0,0 +1,46 @@ +# The Grapefruits Duo + +Frontend client for Eugene, OR based chamber music duo, The Grapefruits Duo. Publicly available at [thegrapefruitsduo.com](https://thegrapefruitsduo.com/). This client consumes a RESTful API built with FastAPI and publicly available at [api.thegrapefruitsduo.com](https://api.thegrapefruitsduo.com/). Back-end source code available on [GitHub](https://github.com/ljensen505/thegrapefruitsduo-back). + +## Features + +The customer-facing page for this SPA is relatively simple. It includes sections for the group itself, each memeber, upcoming events, and a contact form. The admin portal is where the real magic happens. Once authenticated, the user (a member of the group, or myself) can utlilze full CRUD operations on most entities including events, bios, and headshots. + +### Getting started + +```bash +git clone https://github.com/ljensen505/thegrapefruitsduo-front +``` + +```bash +cd thegrapefruitsduo-front +``` + +```bash +npm install +npm run dev +``` + +### Technologies Used + +- React +- TypeScript +- Bootstrap 5 +- Axios +- oauth2 +- Font Awesome +- Cloudinary + +Initialized with Vite. + +### Deployment Info + +- Hosted on a Linode server running Ubuntu 22.04 +- Reverse proxy managed with Nginx on port 6001 +- SSL certificate provided by Let's Encrypt +- Managed with systemd +- DNS managed with Google Domains (for now...) + +### Development Notes + +When running locally, it is expected that the API is running on localhost:8000. This can be changed in the `.env` file (this file may need to be created). See `.env.example` for an example and more info. diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..b8d0556 --- /dev/null +++ b/client/index.html @@ -0,0 +1,27 @@ + + + + + + + The Grapefruits Duo + + + + + + + + + +
+ + + diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..f20ecee --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,3819 @@ +{ + "name": "thegrapefruitsduo", + "version": "0.3.6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "thegrapefruitsduo", + "version": "0.3.6", + "dependencies": { + "@cloudinary/react": "^1.11.2", + "@cloudinary/url-gen": "^1.16.0", + "@fortawesome/free-brands-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", + "@react-oauth/google": "^0.12.1", + "bootstrap": "^5.3.2", + "js-cookie": "^3.0.5", + "jwt-decode": "^4.0.0", + "react": "^18.2.0", + "react-axios": "^2.0.6", + "react-bootstrap": "^2.10.0", + "react-dom": "^18.2.0", + "vite-plugin-package-version": "^1.1.0" + }, + "devDependencies": { + "@types/js-cookie": "^3.0.6", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "prettier": "3.2.5", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cloudinary/html": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@cloudinary/html/-/html-1.11.2.tgz", + "integrity": "sha512-IibBZliI7MOK3dxvz0WlsEyTIsqZhY3SiCBQM0CNwCKxn3N1TO8OgetfU26Now1lJbdoF5JQ8S7Cn6ZWNuU1KA==", + "dependencies": { + "@types/lodash.clonedeep": "^4.5.6", + "@types/lodash.debounce": "^4.0.6", + "@types/node": "^14.14.10", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "typescript": "^4.1.2" + } + }, + "node_modules/@cloudinary/html/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@cloudinary/html/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@cloudinary/react": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@cloudinary/react/-/react-1.11.2.tgz", + "integrity": "sha512-ZnVthMW7TExLMPmyven4N7EVr2oX2yZqnHn6EHBlni9iM0AZd6qtR8R5/SscxNLNwgBieWuvmejUlUT4LqPpMg==", + "dependencies": { + "@cloudinary/html": "^1.11.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@cloudinary/transformation-builder-sdk": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.10.1.tgz", + "integrity": "sha512-UUb1wS/eWCf4YBThGszoBBzH6kP+frdd5JeJkF0/SOwbX3tkcrdzxD+Srn5GXPCqzf6Gw1nrGrv/3U9hiZP55A==", + "dependencies": { + "@cloudinary/url-gen": "^1.7.0" + } + }, + "node_modules/@cloudinary/url-gen": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.16.0.tgz", + "integrity": "sha512-0hbxLuoTGgd555LLZKf8Ut5ey3lIaRTyvtvqD99rLeKUsbq8vrdFoSSwIP+se8pglO5ZSz6y8x6Iu5ddY3wyMw==", + "dependencies": { + "@cloudinary/transformation-builder-sdk": "^1.10.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.1.tgz", + "integrity": "sha512-093l7DAkx0aEtBq66Sf19MgoZewv1zeY9/4C7vSKPO4qMwEsW/2VYTUTpBtLwfb9T2R73tXaRDPmE4UqLCYHfg==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.1.tgz", + "integrity": "sha512-NqzkLFP8ZVI4GSorS0AYljC13QW2sc8bDqJOkBvkAt3M8gbcAXJWVRGtZBCRscki9RZF+rNlnPdg0G0jYkhJcg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-oauth/google": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.1.tgz", + "integrity": "sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.15.tgz", + "integrity": "sha512-cZFXYTxbpzYcieq/mBwSyXgqnGMHoBVh3J7MU0CCoIB4NRZxV9/TuwTBAaLMqpNhC3zTPMCgkQ5Ey07L02Xmcw==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", + "cpu": [ + "ppc64le" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" + }, + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", + "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + }, + "node_modules/@types/react": { + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz", + "integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "peer": true + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "peer": true, + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bootstrap": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", + "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.640", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.640.tgz", + "integrity": "sha512-z/6oZ/Muqk4BaE7P69bXhUhpJbUM9ZJeka43ZwxsDshKtePns4mhBlh8bU5+yrnOnz3fhG82XLzGUXazOmsWnA==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "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", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "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.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz", + "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-axios": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/react-axios/-/react-axios-2.0.6.tgz", + "integrity": "sha512-srQnLZXaW9LDJyC4/qvQ7aPi/rUpsggd3RIM5Q/vFLlQZ4l5bvPtqP/2+UeRXhJH75NfxbcPD3FkjiKONn5V8Q==", + "peerDependencies": { + "axios": "^0.27.2", + "prop-types": "^15.8.1", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.0.tgz", + "integrity": "sha512-87gRP69VAfeU2yKgp8RI3HvzhPNrnYIV2QNranYXataz3ef+k7OhvKGGdxQLQfUsQ2RTmlY66tn4pdFrZ94hNg==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.6", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "optional": true, + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-package-version": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-package-version/-/vite-plugin-package-version-1.1.0.tgz", + "integrity": "sha512-TPoFZXNanzcaKCIrC3e2L/TVRkkRLB6l4RPN/S7KbG7rWfyLcCEGsnXvxn6qR7fyZwXalnnSN/I9d6pSFjHpEA==", + "peerDependencies": { + "vite": ">=2.0.0-beta.69" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..702f260 --- /dev/null +++ b/client/package.json @@ -0,0 +1,44 @@ +{ + "name": "thegrapefruitsduo", + "private": true, + "version": "0.3.6", + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "prettier:check": "npx prettier --check .", + "prettier:fix": "npx prettier --write ." + }, + "dependencies": { + "@cloudinary/react": "^1.11.2", + "@cloudinary/url-gen": "^1.16.0", + "@fortawesome/free-brands-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", + "@react-oauth/google": "^0.12.1", + "bootstrap": "^5.3.2", + "js-cookie": "^3.0.5", + "jwt-decode": "^4.0.0", + "react": "^18.2.0", + "react-axios": "^2.0.6", + "react-bootstrap": "^2.10.0", + "react-dom": "^18.2.0", + "vite-plugin-package-version": "^1.1.0" + }, + "devDependencies": { + "@types/js-cookie": "^3.0.6", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "prettier": "3.2.5", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/client/public/bg.jpeg b/client/public/bg.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d75af72a27a42f17330ad6f2aef68eef661b0f7a GIT binary patch literal 139228 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�*eZ&-oClBgnq2@^?~rY!{eO8MmR@zf!nSzOLp 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%0A`Kr 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 z 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 literal 0 HcmV?d00001 diff --git a/client/public/favicon.ico b/client/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..55efb4ec697c435e7c47b7a73564d8ecc1d94602 GIT binary patch literal 15406 zcmeHOe{fXA9p4OAlH6T#xrCw=As3LKjT8&THH@ubjFp#%-xrrKwi|g% zmyUg3#MoQ27+Z`osDc{deB6@0EM397tX$n^;lU5AJUD{XZ{dzn9m)%{WRca=1ziKgg7MlZ3N2jyt64pLK5zx2+W_8feVK`#35Ui@fq!5w%6~jj zo!IZ_4_sDR*&O@LybmH}(Wg`&{5%!OOVD?5%Jhfm^Wd{_zL}1JuIxydz9Q@LlBpLe zL-G-(uhdPZKFJu>XM03G>i;{{Hb_oG_3>kog7vnoXGF34_sQXhoN9F$tD8~cY+UPD zy5*wdjt*h{X>-#togP;$xli~0Pe4?^+*86!E>>Y3ed_j}gX^7=PX zzYq6+)ynm+xY|9RHY=ZQKIxfqq*X|ypE7lU=Oxf3kKuj*=`7M^$j2yB0I3&guZ;)1 zR=)ymm$|EutM@>b{(|4TQPUz>&bHW8M+8hvY*^4XmA{kV?o)#yX+zcK~%0~&qE z!q=vN{-{Qu*0)^KhrC_Z=oj#T+opj2S&crUJxX{_Gm*?@>^t~B5TTEU?Cbp~+XZ%% zfj;bnT#fx@ zM&GETey^}^`jfD>CEoibAv67C(?*Dm zJ(SIBS_XPQD0O-df#x95owe9Iy8LmEsO91PUajmA)v`}4<&TR+{ONf6$HaXjDM!er zjiycSpw~|{kw%e@I?8=5bCz$c#6I!7w%?3*ui`%wCH(!wbYaVpJ;`9x#-mMnB^ACk z4yX4%$D9WYGU1Xv>p(ZM=Z%Mf_S4t#cMTY5jFAqJEyr5=tY&nR$)|lc(nvhoT3(IZ zAQs5a?+V!uuwfF~_Q?>9D&vyeYe4I)M$hD~)^x3p^_@kvHQCoydm8dVw+ViO=W%}z z=|uQme-M6&?pn#+w4&SNoNW95&-KVp0v9c7K+2?_1kJ_2+Rt0^`A73$Z+{88@KK}} zpu3JCT|%O@cLa9Gf02(oe3R85Vm(@w&j;qQc40xIdGQ<0Zmy*#cpc`o8|(HS_?(Xe z=M^Nw+)wZh|C(}w>QWoFjzC#<(dt?kA`iO31G6j>{f#)fv+W4XWzf7@rUGEIT5nG4d_F$0r1hJX;0>6-h52^2LpWA zmcYIZeScNyOjB9F9!QUW7HmD(3cofjpPPP7L60df=<(D32YTm~>4QHQIj12$;M=(# z^Y@);g#VJh|HtBrj!q-|!*TFu`HVWFYl8uQSbo%LBtMElenRt?2e0n6*PsKHV|w|a zy)taOUritRH|kH?+rX~a4*b({{i3me{@j3+Lm$JW=QI3#gE40|mUfB*3!a+>GIX5$ ztp@q07sO*thcy^7vRfaQsOQ5f4N%Mi#Q~>ai{!E_{957tiyrp)GvZ6}J*n8@so)XV z4wK(|9B}MYq13+y*eC|_sv86Hd&eR+=7eIy5$_B95nXEAeqPvG&k1YirK#bG_Kg&q z0pGn5ZTvUv1y-VbSQkl(u;2Bha4dUF*zemXtUvj1DtI_azWexSuBP$X`E&bEslHl7h@ajwug zQx8KdT_T`S-|`%vX`1VBKG;}V(fn%Zf{i1g^Y|OTDN3qah2wKuMA4=LhM2t@E%7}L%1stzi@N&aH;RNqGa1KQT(G*!rE~$yZA$XZ)F?9 z`GW>7`aXNM$6|Lj&M$Q~t{98AcpG@HgfNFBs5O)+X!)lhlT>hT8p1;*^lLNib;qQcPTk?^qI8z&>^Y0}%mrW?+nc$?R ztFFMs>oEKGSF5y_&x$cJA?)ufY%k@7F y#XxcQtKjW5=&P4qa{VtMZ>`AxdbQ;KysKmV{8dteoq_)pNqtiMlOD)g5Bv|2#SAk5 literal 0 HcmV?d00001 diff --git a/client/src/App.css b/client/src/App.css new file mode 100644 index 0000000..ccd49cb --- /dev/null +++ b/client/src/App.css @@ -0,0 +1,68 @@ +body { + margin: 0; + padding: 0; + + &::before { + content: " "; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: linear-gradient( + rgba(252, 171, 83, 0.3), + rgba(252, 171, 83, 0.3) + ), + url("/bg.jpeg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + will-change: transform; + z-index: -1; + background-color: rgba(249, 255, 63, 0.3); + } + + @media (max-width: 600px) { + &::before { + background-image: none; + background-color: var(--grapefruit-yellow-washed-out); + } + } +} + +.btn-primary { + background-color: var(--group-info-color); + border-color: var(--group-info-color); +} + +.btn-primary:hover { + background-color: var(--group-info-lighter-color); + border-color: var(--group-info-lighter-color); +} + +.btn-primary:disabled { + background-color: var(--group-info-disabled-color); + border-color: var(--group-info-disabled-color); +} + +.btn-primary:focus { + background-color: var(--group-info-darker-color); + border-color: var(--group-info-darker-color); + box-shadow: 0 0 0 0.2rem var(--group-info-darker-color); +} + +.error-text { + background-color: rgb(250, 201, 222); + display: block; + padding: 5px; + border-radius: 5px; + font-weight: bold; +} + +.card { + background-color: transparent !important; + + backdrop-filter: blur(15px); + border: 0px solid transparent !important; + color: var(--group-info-color) !important; +} diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..769d3a7 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,86 @@ +import "./App.css"; +import Musicians from "./Musicians/Musicians"; +import NavBar from "./NavBar/NavBar"; +import ContactForm from "./Forms/Contact/ContactForm"; +import { Container } from "react-bootstrap"; +import Group, { GroupObj } from "./Group/Group"; +import SeriesList, { EventSeriesObj } from "./Series/SeriesList"; +import { useState, useEffect } from "react"; +import Footer from "./Footer/Footer"; +import { getRoot } from "./api"; +import { MusicianObj } from "./Musicians/Musician/Musician"; +import ErrorModal from "./ErrorModal/ErrorModal"; +import Cookies from "js-cookie"; + +function App() { + const tokenCookie = Cookies.get("token"); + const [group, setGroup] = useState(); + const [update, setUpdate] = useState(false); + const [musicians, setMusicians] = useState([]); + const [seriesList, setSeriesList] = useState([]); + const [apiVersion, setApiVersion] = useState(""); + const [error, setError] = useState(""); + const [errorModalShow, setErrorModalShow] = useState(false); + const [errorEntity, setErrorEntity] = useState(""); + const [token, setToken] = useState(tokenCookie ? tokenCookie : ""); + const appVersion = import.meta.env.PACKAGE_VERSION; + + const handleGroupBioChange = () => { + setUpdate(!update); + }; + + const handleError = (error: string, entity: string) => { + console.error(error); + setError(error); + setErrorEntity(entity); + setErrorModalShow(true); + }; + + useEffect(() => { + getRoot() + .then((tgd): void => { + setGroup(tgd.group); + setMusicians(tgd.musicians); + setSeriesList(tgd.events); + setApiVersion(tgd.version); + }) + .catch((error) => { + handleError(error.message, "root"); + }); + }, []); + + return ( +
+ + + + + + + +
+ +
+ ); +} + +export default App; diff --git a/client/src/Auth/Profile.tsx b/client/src/Auth/Profile.tsx new file mode 100644 index 0000000..068a6c9 --- /dev/null +++ b/client/src/Auth/Profile.tsx @@ -0,0 +1,30 @@ +import { Container } from "react-bootstrap"; +import { JwtPayload, jwtDecode } from "jwt-decode"; + +interface GoogleJwtPayload extends JwtPayload { + picture?: string; + name?: string; + email?: string; +} + +interface ProfileProps { + token: string; +} + +const Profile = (props: ProfileProps) => { + const decoded = jwtDecode(props.token); + const name = decoded.name; + const email = decoded.email; + return ( + props.token && ( + +

+ Logged in as: {name} +

+

{email}

+
+ ) + ); +}; + +export default Profile; diff --git a/client/src/Auth/User.tsx b/client/src/Auth/User.tsx new file mode 100644 index 0000000..1e908b8 --- /dev/null +++ b/client/src/Auth/User.tsx @@ -0,0 +1,13 @@ +export class UserObj { + name: string; + email: string; + id: number; + sub?: string; + + constructor(name: string, email: string, id: number, sub?: string) { + this.name = name; + this.email = email; + this.id = id; + this.sub = sub; + } +} diff --git a/client/src/BackgroundStyle.tsx b/client/src/BackgroundStyle.tsx new file mode 100644 index 0000000..8109979 --- /dev/null +++ b/client/src/BackgroundStyle.tsx @@ -0,0 +1,31 @@ +const bgFilter = "rgb(255, 180, 89,0.5)"; +const url = "d18xMDAwLGFyXzE2OjksY19maWxsLGdfYXV0byxlX3NoYXJwZW4=.jpeg"; +const BGStyleMobile = { + backgroundColor: bgFilter, +}; + +const BGStyleDesktop = { + backgroundImage: `linear-gradient(${bgFilter}, ${bgFilter}),` + `url(${url})`, + backgroundRepeat: "no-repeat", + backgroundSize: "cover", + backgroundPosition: "center", + backgroundAttachment: "fixed", +}; + +function isMobile() { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent, + ); +} + +function isFixedBackgroundSupported() { + const testEl = document.createElement("div"); + testEl.style.backgroundAttachment = "fixed"; + return testEl.style.backgroundAttachment === "fixed"; +} + +const BGStyleFinal = + !isMobile() && isFixedBackgroundSupported() ? BGStyleDesktop : BGStyleMobile; + +// const BGStyleFinal = BGStyleDesktop; +export default BGStyleFinal; diff --git a/client/src/Buttons/DeleteButton/DeleteButton.css b/client/src/Buttons/DeleteButton/DeleteButton.css new file mode 100644 index 0000000..984d3f0 --- /dev/null +++ b/client/src/Buttons/DeleteButton/DeleteButton.css @@ -0,0 +1,34 @@ +.delete-icon { + /* position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); */ + font-size: 4rem; + color: rgb(255, 0, 0); +} + +.delete-icon:hover { + color: darkred; + cursor: pointer; +} + +.btn-delete { + background-color: transparent; + border: none; +} + +.btn-delete:hover { + background-color: transparent; + border: none; +} + +.btn-delete:focus { + background-color: transparent; + border: none; + color: lightcoral; +} + +.btn-delete:active { + background-color: transparent; + border: none; +} diff --git a/client/src/Buttons/DeleteButton/DeleteButton.tsx b/client/src/Buttons/DeleteButton/DeleteButton.tsx new file mode 100644 index 0000000..b0519e4 --- /dev/null +++ b/client/src/Buttons/DeleteButton/DeleteButton.tsx @@ -0,0 +1,40 @@ +import { Button } from "react-bootstrap"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faTrash } from "@fortawesome/free-solid-svg-icons"; +import { deleteSeries } from "../../api"; +import "./DeleteButton.css"; +import { EventSeriesObj } from "../../Series/SeriesList"; + +interface DeleteButtonProps { + series: EventSeriesObj; + setSeriesList: React.Dispatch>; + seriesList: EventSeriesObj[]; + actionName?: string; + token: string; +} + +function DeleteButton(props: DeleteButtonProps) { + const handleDelete = () => { + console.log(props.series.series_id); + deleteSeries(props.series.series_id, props.token) + .then(() => { + props.setSeriesList( + props.seriesList.filter( + (series) => series.series_id !== props.series.series_id, + ), + ); + }) + .catch((error) => { + console.error(error); + }); + }; + + return ( + + ); +} + +export default DeleteButton; diff --git a/client/src/Buttons/EditButton/EditButton.css b/client/src/Buttons/EditButton/EditButton.css new file mode 100644 index 0000000..1149e5f --- /dev/null +++ b/client/src/Buttons/EditButton/EditButton.css @@ -0,0 +1,34 @@ +.edit-icon { + /* position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); */ + font-size: 4rem; + color: green; +} + +.edit-icon:hover { + color: darkgreen; + cursor: pointer; +} + +.btn-edit { + background-color: transparent; + border: none; +} + +.btn-edit:hover { + background-color: transparent; + border: none; +} + +.btn-edit:focus { + background-color: transparent; + border: none; + color: var(--group-info-darker-color); +} + +.btn-edit:active { + background-color: transparent; + border: none; +} diff --git a/client/src/Buttons/EditButton/EditButton.tsx b/client/src/Buttons/EditButton/EditButton.tsx new file mode 100644 index 0000000..96350db --- /dev/null +++ b/client/src/Buttons/EditButton/EditButton.tsx @@ -0,0 +1,26 @@ +import { Button } from "react-bootstrap"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; +import "./EditButton.css"; + +interface EditButtonProps { + setModalShow: React.Dispatch>; + faIcon: IconDefinition; + actionName?: string; +} + +function EditButton(props: EditButtonProps) { + return ( + + ); +} + +export default EditButton; diff --git a/client/src/Buttons/Login.tsx b/client/src/Buttons/Login.tsx new file mode 100644 index 0000000..4a770dc --- /dev/null +++ b/client/src/Buttons/Login.tsx @@ -0,0 +1,42 @@ +import { + CredentialResponse, + GoogleLogin, + googleLogout, +} from "@react-oauth/google"; +import { postUser } from "../api"; +import Cookies from "js-cookie"; + +interface LoginProps { + setToken: React.Dispatch>; +} + +function Login(props: LoginProps) { + const handleSuccess = async (credentialResponse: CredentialResponse) => { + if (credentialResponse.credential) { + const token = credentialResponse.credential; + props.setToken(token); + Cookies.set("token", token, { expires: 1 }); + postUser(token) + .then((user) => { + console.log(`Welcome, ${user.name}!`); + }) + .catch(() => { + console.error("Nice try, but you can't do that. Logging you out."); + googleLogout(); + props.setToken(""); + }); + } else { + console.error("Login Failed"); + } + }; + return ( + { + console.error("Login Failed"); + }} + /> + ); +} + +export default Login; diff --git a/client/src/Buttons/Logout.tsx b/client/src/Buttons/Logout.tsx new file mode 100644 index 0000000..fcd8053 --- /dev/null +++ b/client/src/Buttons/Logout.tsx @@ -0,0 +1,25 @@ +import { googleLogout } from "@react-oauth/google"; +import { Button } from "react-bootstrap"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faRightFromBracket } from "@fortawesome/free-solid-svg-icons"; +import Cookies from "js-cookie"; + +interface LogoutProps { + setToken: React.Dispatch>; +} + +function Logout(props: LogoutProps) { + const handleLogout = () => { + googleLogout(); + props.setToken(""); + Cookies.remove("token"); + }; + + return ( + + ); +} + +export default Logout; diff --git a/client/src/Cld/CloudinaryConfig.ts b/client/src/Cld/CloudinaryConfig.ts new file mode 100644 index 0000000..2e99b60 --- /dev/null +++ b/client/src/Cld/CloudinaryConfig.ts @@ -0,0 +1,9 @@ +import { Cloudinary } from "@cloudinary/url-gen/index"; + +const cld = new Cloudinary({ + cloud: { + cloudName: "dreftv0ue", + }, +}); + +export default cld; diff --git a/client/src/EditModals/EditModal.tsx b/client/src/EditModals/EditModal.tsx new file mode 100644 index 0000000..40a9397 --- /dev/null +++ b/client/src/EditModals/EditModal.tsx @@ -0,0 +1,33 @@ +import Modal from "react-bootstrap/Modal"; +import { MusicianProps } from "../Musicians/Musician/Musician"; +import { GroupObj } from "../Group/Group"; +import { EventSeriesObj } from "../Series/SeriesList"; + +interface EditModalProps { + title: string; + show: boolean; + onHide: () => void; + form: JSX.Element; + entity?: MusicianProps | GroupObj | EventSeriesObj; + error?: string; +} + +function EditModal(props: EditModalProps) { + return ( + + + + {props.title} + + + {props.form} + + ); +} + +export default EditModal; diff --git a/client/src/EmailSignupButton/EmailSignupButton.css b/client/src/EmailSignupButton/EmailSignupButton.css new file mode 100644 index 0000000..dcbc891 --- /dev/null +++ b/client/src/EmailSignupButton/EmailSignupButton.css @@ -0,0 +1,12 @@ +#email-signup-button { + background-color: var(--grapefruit-yellow); + color: var(--group-info-darker); +} + +#email-signup-button:hover { + background-color: var(--grapefruit-yellow-washed-out); +} + +#email-signup-button:focus { + background-color: var(--grapefruit-yellow-washed-out); +} diff --git a/client/src/EmailSignupButton/EmailSignupButton.tsx b/client/src/EmailSignupButton/EmailSignupButton.tsx new file mode 100644 index 0000000..6038f07 --- /dev/null +++ b/client/src/EmailSignupButton/EmailSignupButton.tsx @@ -0,0 +1,19 @@ +import { Button } from "react-bootstrap"; +import "./EmailSignupButton.css"; + +function EmailSignupButton() { + const signupUrl = "http://eepurl.com/iNpJz-/"; + return ( + + ); +} + +export default EmailSignupButton; diff --git a/client/src/ErrorModal/ErrorModal.css b/client/src/ErrorModal/ErrorModal.css new file mode 100644 index 0000000..a1c63af --- /dev/null +++ b/client/src/ErrorModal/ErrorModal.css @@ -0,0 +1,8 @@ +.error-modal { + color: rgb(174, 0, 0); + backdrop-filter: blur(10px); +} + +.error-content { + background-color: rgba(255, 201, 201, 0.8); +} diff --git a/client/src/ErrorModal/ErrorModal.tsx b/client/src/ErrorModal/ErrorModal.tsx new file mode 100644 index 0000000..0ab3c9a --- /dev/null +++ b/client/src/ErrorModal/ErrorModal.tsx @@ -0,0 +1,31 @@ +import Modal from "react-bootstrap/Modal"; +import "./ErrorModal.css"; + +interface ErrorModalProps { + error: string; + entity: string; + show: boolean; +} + +function ErrorModal(props: ErrorModalProps) { + return ( + + + API Error + + +

{props.error}

+

error occurred while fetching {props.entity}

+

Try again later or contact the site administrator

+
+
+ ); +} + +export default ErrorModal; diff --git a/client/src/Footer/Footer.css b/client/src/Footer/Footer.css new file mode 100644 index 0000000..4f07edc --- /dev/null +++ b/client/src/Footer/Footer.css @@ -0,0 +1,16 @@ +.footer { + background-image: linear-gradient( + to top, + rgba(139, 215, 210, 0.4) 60%, + transparent + ) !important; + color: var(--group-info-color) !important; +} + +.nav-link { + color: var(--group-info-color) !important; +} + +.nav-link:hover { + color: var(--group-info-lighter-color) !important; +} diff --git a/client/src/Footer/Footer.tsx b/client/src/Footer/Footer.tsx new file mode 100644 index 0000000..093134e --- /dev/null +++ b/client/src/Footer/Footer.tsx @@ -0,0 +1,56 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faInstagram, + faFacebook, + faYoutube, +} from "@fortawesome/free-brands-svg-icons"; +import "./Footer.css"; +import { Container, Row, Col } from "react-bootstrap"; +import { Nav } from "react-bootstrap"; + +const currentYear = new Date().getFullYear(); + +const copyright = ( +

© {currentYear} The Grapefruits Duo

+); + +function Footer() { + return ( +
+ + + + + + + + {copyright} + + +
+ ); +} + +export default Footer; diff --git a/client/src/Forms/Bio/BioForm.tsx b/client/src/Forms/Bio/BioForm.tsx new file mode 100644 index 0000000..240a4b9 --- /dev/null +++ b/client/src/Forms/Bio/BioForm.tsx @@ -0,0 +1,110 @@ +import { Button, Container, Form } from "react-bootstrap"; +import { MusicianObj } from "../../Musicians/Musician/Musician"; +import { GroupObj } from "../../Group/Group"; +import { patchMusician, patchGroup } from "../../api"; +import { useState } from "react"; + +interface EditBioFormProps { + entity: MusicianObj | GroupObj; + hideModal: React.Dispatch>; + onBioChange: () => void; + setGroup?: React.Dispatch>; + setMusician?: React.Dispatch>; + token: string; +} + +function EditBioForm(props: EditBioFormProps) { + const [formBio, setFormBio] = useState(props.entity.bio); + const [canSubmit, setCanSubmit] = useState(false); + const [error, setError] = useState(""); + + const handleBioChange = (event: React.ChangeEvent) => { + setFormBio(event.target.value); + setCanSubmit(true); + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + if (props.entity instanceof MusicianObj) { + updateMusician(props.token, props.entity); + } else if (props.entity instanceof GroupObj) { + updateGroup(props.token, props.entity); + } else { + console.error("Invalid entity type"); + } + props.onBioChange(); + props.hideModal(false); + }; + + const updateMusician = async ( + accessToken: string, + musician: MusicianObj, + ): Promise => { + patchMusician( + musician.id, + formBio, + musician.name, + musician.headshot_id, + accessToken, + ) + .then((patchedMusician) => { + if (props.setMusician) { + props.setMusician(patchedMusician); + } + }) + .catch((error) => { + console.error(error); + setError("Failed to update bio: " + error.response.data.detail); + }); + }; + + const updateGroup = async ( + accessToken: string, + group: GroupObj, + ): Promise => { + patchGroup(group.id, formBio, group.name, accessToken) + .then((patchedGroup) => { + if (props.setGroup) { + props.setGroup(patchedGroup); + } + }) + .catch((error) => { + console.error(error); + setError("Failed to update bio: " + error.response.data.detail); + }); + }; + + const SubmitButton = canSubmit ? ( + + ) : ( + + ); + + return ( +
+ + Bio + + {error && ( + {error} + )} + + + {SubmitButton} + +
+ ); +} + +export default EditBioForm; diff --git a/client/src/Forms/Contact/Confirmation/ConfirmationModal.tsx b/client/src/Forms/Contact/Confirmation/ConfirmationModal.tsx new file mode 100644 index 0000000..5ffc671 --- /dev/null +++ b/client/src/Forms/Contact/Confirmation/ConfirmationModal.tsx @@ -0,0 +1,32 @@ +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; + +interface ConfirmationModalProps { + name: string; + show: boolean; + onHide: () => void; +} + +function ConfirmationModal(props: ConfirmationModalProps) { + return ( + + +

Thank you for your message, {props.name}!

+ +
+
+ ); +} + +export default ConfirmationModal; diff --git a/client/src/Forms/Contact/ContactForm.css b/client/src/Forms/Contact/ContactForm.css new file mode 100644 index 0000000..94f7110 --- /dev/null +++ b/client/src/Forms/Contact/ContactForm.css @@ -0,0 +1,13 @@ +#contact { + padding-top: 70px; + margin-top: -70px; + margin-bottom: 200px; +} + +.contact-title { + margin-bottom: 6rem; +} + +.contact-text { + color: var(--group-info-color); +} diff --git a/client/src/Forms/Contact/ContactForm.tsx b/client/src/Forms/Contact/ContactForm.tsx new file mode 100644 index 0000000..3bc3f57 --- /dev/null +++ b/client/src/Forms/Contact/ContactForm.tsx @@ -0,0 +1,105 @@ +import Button from "react-bootstrap/Button"; +import Form from "react-bootstrap/Form"; +import { postMessage } from "../../api"; +import { useState } from "react"; +import { Col, Container, Row } from "react-bootstrap"; +import "./ContactForm.css"; +import ConfirmationModal from "./Confirmation/ConfirmationModal"; +import EmailSignupButton from "../../EmailSignupButton/EmailSignupButton"; + +function ContactForm() { + const [confirmationModalShow, setConfirmationModalShow] = useState(false); + const [form, setForm] = useState<{ [key: string]: string }>({ + name: "", + email: "", + message: "", + }); + + const handleFormChange = ( + event: React.ChangeEvent, + ) => { + setForm({ ...form, [event.target.name]: event.target.value }); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + postMessage(form.name, form.email, form.message) + .then(() => { + setConfirmationModalShow(true); + }) + .catch((error) => { + console.error(error); + }); + }; + + const handleFormReset = () => { + setForm({ name: "", email: "", message: "" }); + setConfirmationModalShow(false); + }; + + return ( + + + +
+

Contact Us

+ + + + + + + + + Message + + + +
+ + +
+ + + + + +
+ ); +} + +export default ContactForm; diff --git a/client/src/Forms/Event/EventForm.css b/client/src/Forms/Event/EventForm.css new file mode 100644 index 0000000..db3dad7 --- /dev/null +++ b/client/src/Forms/Event/EventForm.css @@ -0,0 +1,5 @@ +.event-form-container { + border: 2px solid var(--group-info-color); + border-radius: 5px; + margin-top: 5px; +} diff --git a/client/src/Forms/Event/EventForm.tsx b/client/src/Forms/Event/EventForm.tsx new file mode 100644 index 0000000..bce1ee9 --- /dev/null +++ b/client/src/Forms/Event/EventForm.tsx @@ -0,0 +1,246 @@ +import { useState } from "react"; +import { Button, Container, Form } from "react-bootstrap"; +import { postSeries, putSeries } from "../../api"; +import { EventSeriesObj } from "../../Series/SeriesList"; +import { EventObj } from "../../Series/Events.tsx/Event/Event"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faTrash } from "@fortawesome/free-solid-svg-icons"; +import "./EventForm.css"; + +interface AddEventFormProps { + setModalShow: React.Dispatch>; + setSeriesList: React.Dispatch>; + seriesList: EventSeriesObj[]; + isNewSeries: boolean; + series?: EventSeriesObj; + setSeries?: React.Dispatch>; + token: string; +} + +function EventForm(props: AddEventFormProps) { + /* + this form serves two purposes: + 1. Create a new series + 2. Edit an existing series + + if isNewSeries is true,an empty form will be rendered to create a new series and post it to the server + if isNewSeries is false, the form will be populated with existing series data and will be used to edit the series + */ + if (props.series && props.isNewSeries) { + throw new Error("series provided for new series form"); + } + const [formEvents, setFormEvents] = useState<(EventObj | undefined)[]>( + props.series === undefined + ? [undefined] + : props.series.events.map((event) => event), + ); + const [postError, setPostError] = useState(""); + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + const form = event.target as HTMLFormElement; + const formElements = form.elements as HTMLFormControlsCollection; + const seriesNameElement = formElements.namedItem( + "formSeriesName", + ) as HTMLInputElement; + + const seriesName = seriesNameElement.value; + const seriesDescriptionElement = formElements.namedItem( + "formSeriesDescription", + ) as HTMLInputElement; + const seriesDescription = seriesDescriptionElement.value; + const eventObjects: EventObj[] = findEventElements(formElements); + + const series = new EventSeriesObj( + (props.series && props.series.series_id) || 0, + seriesName, + seriesDescription, + eventObjects, + (props.series && props.series.poster_id) || undefined, + ); + + if (!props.token) { + console.error("no access token"); + return; + } + if (props.isNewSeries) { + postSeries(series, props.token) + .then((newSeries) => { + props.setSeriesList([...props.seriesList, newSeries]); + props.setModalShow(false); + }) + .catch((error) => { + console.error(error); + setPostError( + "Failed to create series: " + error.response.data.detail, + ); + }); + } else { + putSeries(series, props.token) + .then((updatedSeries) => { + props.setSeries?.(updatedSeries); + props.setModalShow(false); + }) + .catch((error) => { + console.error(error); + setPostError( + "Failed to update series: " + error.response.data.detail, + ); + }); + } + }; + + function findEventElements( + formElements: HTMLFormControlsCollection, + ): EventObj[] { + const events: EventObj[] = []; + for (let idx = 0; idx < formEvents.length; idx++) { + const locationElement = formElements.namedItem( + `formEvent${idx}Location`, + ) as HTMLInputElement; + const timeElement = formElements.namedItem( + `formEvent${idx}Time`, + ) as HTMLInputElement; + const ticketUrlElement = formElements.namedItem( + `formEvent${idx}TicketUrl`, + ) as HTMLInputElement; + const mapUrlElement = formElements.namedItem( + `formEvent${idx}MapUrl`, + ) as HTMLInputElement; + const location = locationElement.value; + const time = timeElement.value; + const ticketUrl = ticketUrlElement.value + ? ticketUrlElement.value + : undefined; + const mapUrl = mapUrlElement.value ? mapUrlElement.value : undefined; + const event = new EventObj(0, location, time, ticketUrl, mapUrl); + events.push(event); + } + return events; + } + + const AddEventButton = ( + + + + ); + + const SubmitButton = ( + + ); + + return ( +
+ + Series Name * + + + + Series Description * + + + + {formEvents.map((event, idx) => ( + + {`Event ${idx}`} + + Event Location * + + + + Event Time * + + + + Event Ticket URL + + + + Event Map URL + + + + + + + ))} + + {AddEventButton} + + + A poster can be added after returning to the homepage. + + {postError && ( + {postError} + )} + + + {SubmitButton} + +
+ ); +} + +export default EventForm; diff --git a/client/src/Forms/HeadshotUpload/HeadshotUpload.css b/client/src/Forms/HeadshotUpload/HeadshotUpload.css new file mode 100644 index 0000000..09250e9 --- /dev/null +++ b/client/src/Forms/HeadshotUpload/HeadshotUpload.css @@ -0,0 +1,8 @@ +.headshot-preview { + max-width: 400px; + max-height: 400px; +} + +#headshot-upload { + display: block; +} diff --git a/client/src/Forms/HeadshotUpload/HeadshotUploadForm.tsx b/client/src/Forms/HeadshotUpload/HeadshotUploadForm.tsx new file mode 100644 index 0000000..a8c8ae1 --- /dev/null +++ b/client/src/Forms/HeadshotUpload/HeadshotUploadForm.tsx @@ -0,0 +1,123 @@ +import { Form, Image, Button, Container } from "react-bootstrap"; +import { MusicianObj } from "../../Musicians/Musician/Musician"; +import { useState } from "react"; +import { postHeadshot } from "../../api"; +import "./HeadshotUpload.css"; + +export const sizeLimit = 1000000; // one megabyte + +interface HeadshotUploadProps { + currentHeadshot: string; + musician: MusicianObj; + onHeadshotChange?: () => void; + hideModal?: React.Dispatch>; + setMusician?: React.Dispatch>; + token: string; +} + +function HeadshotUpload(props: HeadshotUploadProps) { + const [selectedFile, setSelectedFile] = useState(null); + const [fileError, setFileError] = useState(""); + const [preview, setPreview] = useState(props.currentHeadshot); + const [canSubmit, setCanSubmit] = useState(false); + + const handleFileChange = (event: React.ChangeEvent) => { + const allowedTypes = ["image/jpeg", "image/png"]; + const file = event.target.files?.[0]; + const fileSize = file?.size; // bytes + const fileType = file?.type; // MIME type + + if (fileSize && fileSize > sizeLimit) { + console.error("file too large"); + setFileError("file too large"); + setCanSubmit(false); + return; + } + if (fileType && !allowedTypes.includes(fileType)) { + console.error("invalid file type"); + setFileError("invalid file type"); + setCanSubmit(false); + return; + } + + if (file) { + setSelectedFile(file); + const reader = new FileReader(); + reader.onloadend = () => { + setPreview(reader.result as string); + }; + reader.readAsDataURL(file); + setCanSubmit(true); + setFileError(""); + } + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + if (!props.token) { + console.error("no access token"); + return; + } + + if (props.musician && selectedFile) { + uploadHeadshot(props.token, props.musician, selectedFile); + props.hideModal?.(false); + + return; + } + console.error("no file selected"); + }; + + const uploadHeadshot = async ( + accessToken: string, + musician: MusicianObj, + file: File, + ) => { + postHeadshot(musician.id, file, accessToken) + .then((updatedMusician) => { + props.onHeadshotChange?.(); + props.setMusician?.(updatedMusician); + }) + .catch((error) => { + console.error(error); + }); + }; + + const SubmitButton = canSubmit ? ( + + ) : ( + + ); + + return ( +
+ + + {`${props.musician.name} + + Upload Headshot + + + size limit: {sizeLimit / 1000000} MB + + {fileError && ( + {fileError} + )} + + + {SubmitButton} + +
+ ); +} + +export default HeadshotUpload; diff --git a/client/src/Forms/PosterUpload/PosterUploadForm.tsx b/client/src/Forms/PosterUpload/PosterUploadForm.tsx new file mode 100644 index 0000000..ec4fa61 --- /dev/null +++ b/client/src/Forms/PosterUpload/PosterUploadForm.tsx @@ -0,0 +1,117 @@ +import { Button, Container, Form, Image } from "react-bootstrap"; +import { EventSeriesObj } from "../../Series/SeriesList"; +import { useState } from "react"; +import { sizeLimit } from "../HeadshotUpload/HeadshotUploadForm"; +import { postSeriesPoster } from "../../api"; + +interface PosterUploadFormProps { + series: EventSeriesObj; + currentPoster: string | undefined; + setModalShow: React.Dispatch>; + setSeries: React.Dispatch>; + token: string; +} + +function PosterUploadForm(props: PosterUploadFormProps) { + const [preview, setPreview] = useState( + props.currentPoster, + ); + const [fileError, setFileError] = useState(""); + const [canSubmit, setCanSubmit] = useState(false); + const [selectedFile, setSelectedFile] = useState(null); + + const handleFileChange = (event: React.ChangeEvent) => { + const allowedTypes = ["image/jpeg", "image/png"]; + const file = event.target.files?.[0]; + const fileSize = file?.size; // bytes + const fileType = file?.type; // MIME type + + if (fileSize && fileSize > sizeLimit) { + console.error("file too large"); + setFileError("file too large"); + setCanSubmit(false); + return; + } + if (fileType && !allowedTypes.includes(fileType)) { + console.error("invalid file type"); + setFileError("invalid file type"); + setCanSubmit(false); + return; + } + + if (file) { + setSelectedFile(file); + const reader = new FileReader(); + reader.onloadend = () => { + setPreview(reader.result as string); + }; + reader.readAsDataURL(file); + setCanSubmit(true); + setFileError(""); + } + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + if (!props.token) { + console.error("no access token"); + return; + } + + if (selectedFile) { + postSeriesPoster(props.series.series_id, selectedFile, props.token) + .then((updatedEventObj) => { + props.setSeries(updatedEventObj); + props.setModalShow(false); + }) + .catch((error) => { + console.error(error); + setFileError( + "Failed to upload poster: " + error.response.data.detail, + ); + }); + return; + } + console.error("no file selected"); + }; + + const SubmitButton = canSubmit ? ( + + ) : ( + + ); + + return ( +
+ + + {preview && ( + {`${props.series.name} + )} + + Upload Poster + + + size limit: {sizeLimit / 1000000} MB + + {fileError && ( + {fileError} + )} + + + {SubmitButton} + +
+ ); +} + +export default PosterUploadForm; diff --git a/client/src/Group/Group.css b/client/src/Group/Group.css new file mode 100644 index 0000000..871d8c7 --- /dev/null +++ b/client/src/Group/Group.css @@ -0,0 +1,13 @@ +.group-info { + color: var(--group-info-color); +} + +.group-bio { + color: var(--group-info-color); + font-weight: 600 !important; +} + +#about { + padding-top: 120px; + margin-top: -70px; +} diff --git a/client/src/Group/Group.tsx b/client/src/Group/Group.tsx new file mode 100644 index 0000000..d11934d --- /dev/null +++ b/client/src/Group/Group.tsx @@ -0,0 +1,79 @@ +import { Card, Container } from "react-bootstrap"; +import "./Group.css"; +import EditModal from "../EditModals/EditModal"; +import { useState } from "react"; +import EditBioForm from "../Forms/Bio/BioForm"; +import { faPen } from "@fortawesome/free-solid-svg-icons"; +import EditButton from "../Buttons/EditButton/EditButton"; + +export class GroupObj { + id: number; + name: string; + bio: string; + + constructor(id: number, name: string, bio: string) { + this.id = id; + this.name = name; + this.bio = bio; + } +} + +interface GroupProps { + group?: GroupObj; + onBioChange: () => void; + setGroup: React.Dispatch>; + token: string; +} + +function Group(props: GroupProps) { + const [modalShow, setModalShow] = useState(false); + if (!props.group) { + return null; + } + const group = props.group; + + const EditTitle = `Edit ${group.name}'s Bio`; + + const EditIcon = ( + + + setModalShow(false)} + title={EditTitle} + entity={group} + form={ + + } + /> + + ); + + return ( +
+ + + + +

{group.name}

+
+ {props.token && EditIcon} + {group.bio} +
+
+
+
+ ); +} + +export default Group; diff --git a/client/src/Musicians/Musician/Bio/Bio.tsx b/client/src/Musicians/Musician/Bio/Bio.tsx new file mode 100644 index 0000000..1e7a116 --- /dev/null +++ b/client/src/Musicians/Musician/Bio/Bio.tsx @@ -0,0 +1,63 @@ +import EditButton from "../../../Buttons/EditButton/EditButton"; +import { useEffect, useState } from "react"; +import { Col, Card, Container } from "react-bootstrap"; +import EditBioForm from "../../../Forms/Bio/BioForm"; +import EditModal from "../../../EditModals/EditModal"; +import { MusicianObj } from "../Musician"; +import { faPen } from "@fortawesome/free-solid-svg-icons"; + +interface BioProps { + musician: MusicianObj; + textPosition: string; + onBioChange: () => void; + setMusician: React.Dispatch>; + token: string; +} + +function MusicianBio(props: BioProps) { + const [modalShow, setModalShow] = useState(false); + const EditTitle = `Edit ${props.musician.name}'s Bio`; + + useEffect(() => { + props.setMusician(props.musician); + }, [props]); + + const Editable = ( + + + setModalShow(false)} + title={EditTitle} + entity={props.musician} + form={ + + } + /> + + ); + + return ( + + + {props.musician.name} + + {props.token && Editable} + {props.musician.bio} + + + + ); +} + +export default MusicianBio; diff --git a/client/src/Musicians/Musician/Headshot/Headshot.css b/client/src/Musicians/Musician/Headshot/Headshot.css new file mode 100644 index 0000000..e69de29 diff --git a/client/src/Musicians/Musician/Headshot/Headshot.tsx b/client/src/Musicians/Musician/Headshot/Headshot.tsx new file mode 100644 index 0000000..7e3a82f --- /dev/null +++ b/client/src/Musicians/Musician/Headshot/Headshot.tsx @@ -0,0 +1,64 @@ +import { faUpload } from "@fortawesome/free-solid-svg-icons"; +import EditButton from "../../../Buttons/EditButton/EditButton"; +import { Col, Container, Image } from "react-bootstrap"; +import HeadshotUpload from "../../../Forms/HeadshotUpload/HeadshotUploadForm"; +import EditModal from "../../../EditModals/EditModal"; +import { useState } from "react"; +import { MusicianObj } from "../Musician"; +import "./Headshot.css"; + +export interface HeadshotProps { + src: string; + musician: MusicianObj; + onHeadshotChange?: () => void; + setMusician?: React.Dispatch>; + token: string; +} + +function Headshot(props: HeadshotProps) { + const [modalShow, setModalShow] = useState(false); + + const EditableHeadshot = ( + + + setModalShow(false)} + title="Edit Headshot" + entity={props.musician} + form={ + + } + /> + + ); + + return ( + + + {props.musician.name} + {props.token && EditableHeadshot} + + + ); +} + +export default Headshot; diff --git a/client/src/Musicians/Musician/Musician.css b/client/src/Musicians/Musician/Musician.css new file mode 100644 index 0000000..7b62a12 --- /dev/null +++ b/client/src/Musicians/Musician/Musician.css @@ -0,0 +1,9 @@ +.musician-container { + margin-bottom: 6rem; + padding-top: 70px; + margin-top: -70px; +} +/* +.musician-card { + background-color: transparent !important; +} */ diff --git a/client/src/Musicians/Musician/Musician.tsx b/client/src/Musicians/Musician/Musician.tsx new file mode 100644 index 0000000..3d46070 --- /dev/null +++ b/client/src/Musicians/Musician/Musician.tsx @@ -0,0 +1,68 @@ +import { Container, Row } from "react-bootstrap"; +import cld from "../../Cld/CloudinaryConfig"; +import "./Musician.css"; +import Headshot from "./Headshot/Headshot"; +import MusicianBio from "./Bio/Bio"; +import { useState } from "react"; + +export class MusicianObj { + id: number; + name: string; + bio: string; + headshot_id: string; + + constructor(id: number, name: string, bio: string, headshot_id: string) { + this.id = id; + this.name = name; + this.bio = bio; + this.headshot_id = headshot_id; + } +} + +export interface MusicianProps { + musician: MusicianObj; + onBioChange: () => void; + onHeadshotChange?: () => void; + token: string; +} + +function Musician(props: MusicianProps) { + const [musician, setMusician] = useState(props.musician); + const textPosition = musician.id % 2 === 0 ? "text-end" : "text-start"; + const image = cld.image(musician.headshot_id); + const imgUrl = image.toURL(); + const musicianID = musician.name.split(" ").join("-").toLowerCase(); + const key = `musician-${musician.id}`; + + const bioCard = ( + + ); + + const headshot = ( + + ); + + return ( + + + {musician.id % 2 === 0 ? [bioCard, headshot] : [headshot, bioCard]} + + + ); +} + +export default Musician; diff --git a/client/src/Musicians/Musicians.css b/client/src/Musicians/Musicians.css new file mode 100644 index 0000000..b739877 --- /dev/null +++ b/client/src/Musicians/Musicians.css @@ -0,0 +1,10 @@ +.musicians-title { + color: var(--group-info-color); + margin-bottom: 6rem; +} + +#musicians { + padding-top: 70px; + margin-top: -70px; + margin-bottom: 400px; +} diff --git a/client/src/Musicians/Musicians.tsx b/client/src/Musicians/Musicians.tsx new file mode 100644 index 0000000..3bf4c8a --- /dev/null +++ b/client/src/Musicians/Musicians.tsx @@ -0,0 +1,47 @@ +import { useState } from "react"; +import Musician, { MusicianObj } from "./Musician/Musician"; +import { Col, Container } from "react-bootstrap"; +import "./Musicians.css"; + +interface MusiciansProps { + musicians: MusicianObj[]; + setMusicians: React.Dispatch>; + token: string; +} + +function Musicians(props: MusiciansProps) { + const [update, setUpdate] = useState(false); + const musicians = props.musicians; + + const handleBioChange = () => { + setUpdate(!update); + }; + const handleHeadshotChange = () => { + setUpdate(!update); + }; + + const musicianList = musicians.map((musician) => ( + + )); + + return ( +
+ + +

+ Meet the Musicians +

+
+ {musicianList} + +
+ ); +} + +export default Musicians; diff --git a/client/src/NavBar/AdminDropdown/AdminDropdown.css b/client/src/NavBar/AdminDropdown/AdminDropdown.css new file mode 100644 index 0000000..0337fa2 --- /dev/null +++ b/client/src/NavBar/AdminDropdown/AdminDropdown.css @@ -0,0 +1,7 @@ +#api-container { + font-size: smaller; +} + +#auth-row { + background-color: var(--grapefruit-yellow-washed-out); +} diff --git a/client/src/NavBar/AdminDropdown/AdminDropdown.tsx b/client/src/NavBar/AdminDropdown/AdminDropdown.tsx new file mode 100644 index 0000000..a71c3bf --- /dev/null +++ b/client/src/NavBar/AdminDropdown/AdminDropdown.tsx @@ -0,0 +1,49 @@ +import { ButtonGroup, Container } from "react-bootstrap"; +import Dropdown from "react-bootstrap/Dropdown"; +import DropdownButton from "react-bootstrap/DropdownButton"; +import Profile from "../../Auth/Profile"; +import Login from "../../Buttons/Login"; +import Logout from "../../Buttons/Logout"; +import "./AdminDropdown.css"; + +interface AdminDropdownProps { + appVersion: string; + apiVersion: string; + token: string; + setToken: React.Dispatch>; +} + +function AdminDropdown(props: AdminDropdownProps) { + const AuthButton = () => { + return props.token ? ( + + ) : ( + + ); + }; + + return ( + <> + + {props.token && } + + + + + +

APP Version: {props.appVersion}

+

API Version: {props.apiVersion}

+
+
+ + ); +} + +export default AdminDropdown; diff --git a/client/src/NavBar/NavBar.css b/client/src/NavBar/NavBar.css new file mode 100644 index 0000000..0964cfa --- /dev/null +++ b/client/src/NavBar/NavBar.css @@ -0,0 +1,36 @@ +.navbar-color { + backdrop-filter: blur(10px); + transition: background-color 0.5s ease; +} + +.navbar-text-color .dropdown-toggle { + color: var(--group-info-color); +} + +.navbar-scrolled { + background-color: var(--grapefruit-yellow); +} + +.logo { + height: 1.7rem; + width: auto; +} + +#nav-contact { + margin-right: 8px; +} + +#admin-dropdown { + text-decoration: none; + text-align: left; + padding-left: 0; +} + +#admin-dropdown:hover { + text-decoration: none; + color: var(--group-info-lighter-color); +} + +.dropdown-menu { + background-color: var(--grapefruit-yellow-washed-out); +} diff --git a/client/src/NavBar/NavBar.tsx b/client/src/NavBar/NavBar.tsx new file mode 100644 index 0000000..33f7910 --- /dev/null +++ b/client/src/NavBar/NavBar.tsx @@ -0,0 +1,91 @@ +import Container from "react-bootstrap/Container"; +import Nav from "react-bootstrap/Nav"; +import Navbar from "react-bootstrap/Navbar"; +import "./NavBar.css"; +import { useState, useEffect } from "react"; +import AdminDropdown from "./AdminDropdown/AdminDropdown"; +import { Image, NavDropdown } from "react-bootstrap"; +import { MusicianObj } from "../Musicians/Musician/Musician"; + +interface NavBarProps { + musicians: MusicianObj[]; + appVersion: string; + apiVersion: string; + token: string; + setToken: React.Dispatch>; +} + +function NavBar(props: NavBarProps) { + const [scrolled, setScrolled] = useState(false); + + const MusicianLinks = props.musicians.map((musician) => ( + + {musician.name} + + )); + + useEffect(() => { + const handleScroll = () => { + const isScrolled = window.scrollY > 0; + if (isScrolled !== scrolled) { + setScrolled(!scrolled); + } + }; + + document.addEventListener("scroll", handleScroll); + return () => { + document.removeEventListener("scroll", handleScroll); + }; + }, [scrolled]); + + return ( + + + + TGD Logo + + + + + + + + ); +} + +export default NavBar; diff --git a/client/src/Series/Events.tsx/Event/Event.css b/client/src/Series/Events.tsx/Event/Event.css new file mode 100644 index 0000000..7ea3663 --- /dev/null +++ b/client/src/Series/Events.tsx/Event/Event.css @@ -0,0 +1,5 @@ +.event-list-item { + background-color: transparent !important; + color: inherit; + border: 0.15rem solid var(--group-info-color); +} diff --git a/client/src/Series/Events.tsx/Event/Event.tsx b/client/src/Series/Events.tsx/Event/Event.tsx new file mode 100644 index 0000000..e7450c7 --- /dev/null +++ b/client/src/Series/Events.tsx/Event/Event.tsx @@ -0,0 +1,70 @@ +import { ListGroup } from "react-bootstrap"; +import "./Event.css"; + +export class EventObj { + event_id: number; + location: string; + time: string; // ISO 8601 formatted date-time string + ticket_url?: string; + map_url?: string; + + constructor( + event_id: number, + location: string, + time: string, + ticket_url?: string, + map_url?: string, + ) { + this.event_id = event_id; + this.location = location; + this.time = time; + this.ticket_url = ticket_url; + this.map_url = map_url; + } +} + +interface EventProps { + event: EventObj; +} + +function Event(props: EventProps) { + const event = props.event; + const date = new Date(event.time); + const dateString = date.toLocaleDateString(undefined, { + month: "long", + day: "numeric", + weekday: "long", + }); + const location = event.map_url ? ( + <> +
+ {event.location} + + + ) : ( + event.location + ); + const tickets = event.ticket_url ? ( + <> + |{" "} + + Tickets + + + ) : null; + const timeString = date.toLocaleTimeString(undefined, { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + + return ( + +

+ {dateString} {timeString.toLowerCase()} | {location} {tickets} +

+
+ ); +} + +export default Event; diff --git a/client/src/Series/Events.tsx/Events.css b/client/src/Series/Events.tsx/Events.css new file mode 100644 index 0000000..c1c0cb3 --- /dev/null +++ b/client/src/Series/Events.tsx/Events.css @@ -0,0 +1,3 @@ +.event-list { + margin-top: 2.5rem; +} diff --git a/client/src/Series/Events.tsx/Events.tsx b/client/src/Series/Events.tsx/Events.tsx new file mode 100644 index 0000000..7da9c3b --- /dev/null +++ b/client/src/Series/Events.tsx/Events.tsx @@ -0,0 +1,23 @@ +import { ListGroup } from "react-bootstrap"; +import Event, { EventObj } from "./Event/Event"; +import "./Events.css"; + +interface EventsProps { + events: EventObj[]; +} + +function Events(props: EventsProps) { + const events = props.events; + + return ( + <> + + {events.map((event) => ( + + ))} + + + ); +} + +export default Events; diff --git a/client/src/Series/Series/Series.tsx b/client/src/Series/Series/Series.tsx new file mode 100644 index 0000000..10a5f7a --- /dev/null +++ b/client/src/Series/Series/Series.tsx @@ -0,0 +1,90 @@ +import { Card, Col, Container, Row } from "react-bootstrap"; +import { EventSeriesObj } from "../SeriesList"; +import Events from "../Events.tsx/Events"; +import SeriesPoster from "./SeriesPoster"; +import EditButton from "../../Buttons/EditButton/EditButton"; +import DeleteButton from "../../Buttons/DeleteButton/DeleteButton"; +import { useEffect, useState } from "react"; +import { faEdit } from "@fortawesome/free-solid-svg-icons"; +import EditModal from "../../EditModals/EditModal"; +import EventForm from "../../Forms/Event/EventForm"; + +interface SeriesProps { + series: EventSeriesObj; + setSeriesList: React.Dispatch>; + seriesList: EventSeriesObj[]; + token: string; +} + +function Series(props: SeriesProps) { + const [series, setSeries] = useState(props.series); + const [modalShow, setModalShow] = useState(false); + + useEffect(() => { + setSeries(props.series); + }, [props.series]); + + const EditableSeries = ( + + + + + + setModalShow(false)} + title="Edit Concert Series" + entity={props.series} + form={ + + } + /> + + + + + + + + + + + ); + + return ( + + + + + + + +

{series.name}

+ {props.token && EditableSeries} +

{series.description}

+ +
+
+ +
+ ); +} + +export default Series; diff --git a/client/src/Series/Series/SeriesPoster.tsx b/client/src/Series/Series/SeriesPoster.tsx new file mode 100644 index 0000000..8646f1e --- /dev/null +++ b/client/src/Series/Series/SeriesPoster.tsx @@ -0,0 +1,57 @@ +import { Container, Image } from "react-bootstrap"; +import { EventSeriesObj } from "../SeriesList"; +import cld from "../../Cld/CloudinaryConfig"; +import { useState } from "react"; +import EditButton from "../../Buttons/EditButton/EditButton"; +import { faUpload } from "@fortawesome/free-solid-svg-icons"; +import EditModal from "../../EditModals/EditModal"; +import PosterUploadForm from "../../Forms/PosterUpload/PosterUploadForm"; + +interface SeriesPosterProps { + series: EventSeriesObj; + setSeries: React.Dispatch>; + token: string; +} + +function SeriesPoster(props: SeriesPosterProps) { + const series = props.series; + const imgUrl = cld.image(series.poster_id).toURL(); + const imgSrc = imgUrl ? imgUrl : undefined; + const [modalShow, setModalShow] = useState(false); + + const EditablePoster = ( + + + setModalShow(false)} + title="Edit Poster" + entity={props.series} + form={ + + } + /> + + ); + + return ( + + {series.poster_id ? ( + {series.name} + ) : null} + {props.token && EditablePoster} + + ); +} + +export default SeriesPoster; diff --git a/client/src/Series/SeriesList.css b/client/src/Series/SeriesList.css new file mode 100644 index 0000000..b6b6273 --- /dev/null +++ b/client/src/Series/SeriesList.css @@ -0,0 +1,19 @@ +.events-title { + color: var(--group-info-color); + margin-bottom: 6rem; +} + +hr { + border: 0; + height: 4px; /* This controls the thickness of the divider */ + background: rgba(124, 1, 68); /* This sets the color of the divider */ + margin-top: 10rem; + margin-bottom: 6rem; + opacity: 0.9; +} + +#events { + padding-top: 70px; + margin-top: -70px; + margin-bottom: 400px; +} diff --git a/client/src/Series/SeriesList.tsx b/client/src/Series/SeriesList.tsx new file mode 100644 index 0000000..635c17e --- /dev/null +++ b/client/src/Series/SeriesList.tsx @@ -0,0 +1,106 @@ +import { Col, Container } from "react-bootstrap"; +import { EventObj } from "./Events.tsx/Event/Event"; +import Series from "./Series/Series"; +import "./SeriesList.css"; +import EditButton from "../Buttons/EditButton/EditButton"; +import { useState } from "react"; +import { faAdd } from "@fortawesome/free-solid-svg-icons"; +import EditModal from "../EditModals/EditModal"; +import EventForm from "../Forms/Event/EventForm"; + +export class EventSeriesObj { + series_id: number; + name: string; + description: string; + events: EventObj[]; + poster_id?: string; // Cloudinary public ID + + constructor( + series_id: number, + name: string, + description: string, + events: EventObj[], + poster_id?: string, + ) { + this.series_id = series_id; + this.name = name; + this.description = description; + this.events = events; + this.poster_id = poster_id; + } +} + +interface SeriesListProps { + seriesList: EventSeriesObj[]; + setSeriesList: React.Dispatch>; + token: string; +} + +function SeriesList(props: SeriesListProps) { + const seriesList = props.seriesList; + const [modalShow, setModalShow] = useState(false); + + const AddableSeries = ( + + + setModalShow(false)} + title="Add Concert Series" + form={ + + } + /> + + ); + + if (seriesList.length === 0) { + return ( +
+ + +

Upcoming Events

+ {props.token && AddableSeries} +
+ +

Stay tuned!

+
+ +
+ ); + } + + return ( +
+ + +

Upcoming Events

+ {props.token && AddableSeries} +
+ {seriesList.map((series, idx) => ( + + + {idx < seriesList.length - 1 &&
} +
+ ))} + +
+ ); +} + +export default SeriesList; diff --git a/client/src/api.tsx b/client/src/api.tsx new file mode 100644 index 0000000..2f4f1b3 --- /dev/null +++ b/client/src/api.tsx @@ -0,0 +1,293 @@ +import axios from "axios"; +import { GroupObj } from "./Group/Group"; +import { MusicianObj } from "./Musicians/Musician/Musician"; +import { EventSeriesObj } from "./Series/SeriesList"; +import { EventObj } from "./Series/Events.tsx/Event/Event"; +import { UserObj } from "./Auth/User"; + +const baseURL = import.meta.env.VITE_API_URL as string; + +class TheGrapefruitsDuoAPI { + version: string; + group: GroupObj; + musicians: MusicianObj[]; + events: EventSeriesObj[]; + + constructor( + version: string, + group: GroupObj, + musicians: MusicianObj[], + events: EventSeriesObj[], + ) { + this.version = version; + this.group = group; + this.musicians = musicians; + this.events = events; + } +} + +const api = axios.create({ + baseURL: baseURL, +}); + +export const getRoot = async (): Promise => { + const response = await api.get("/"); + const tgd = new TheGrapefruitsDuoAPI( + response.data.version, + new GroupObj( + response.data.group.id, + response.data.group.name, + response.data.group.bio, + ), + response.data.musicians.map( + (musician: MusicianObj) => + new MusicianObj( + musician.id, + musician.name, + musician.bio, + musician.headshot_id, + ), + ), + response.data.events.map( + (series: EventSeriesObj) => + new EventSeriesObj( + series.series_id, + series.name, + series.description, + series.events.map( + (event: EventObj) => + new EventObj( + event.event_id, + event.location, + event.time, + event.ticket_url, + event.map_url, + ), + ), + series.poster_id, + ), + ), + ); + console.log(tgd); + return tgd; +}; + +export const getUsers = async (): Promise => { + const response = await api.get("/users/"); + return response.data.map( + (user: UserObj) => new UserObj(user.name, user.email, user.id, user.sub), + ); +}; + +export const getUser = async (id: number): Promise => { + const response = await api.get(`/users/${id}/`); + return new UserObj( + response.data.name, + response.data.email, + response.data.id, + response.data.sub, + ); +}; + +export const postUser = async (token: string): Promise => { + const response = await api.post( + "/users/", + {}, + { headers: { Authorization: `Bearer ${token}` } }, + ); + return new UserObj( + response.data.name, + response.data.email, + response.data.id, + response.data.sub, + ); +}; + +export const getGroup = async (): Promise => { + const response = await api.get("/group/"); + return new GroupObj(response.data.id, response.data.name, response.data.bio); +}; + +export const getMusicians = async (): Promise => { + const response = await api.get("/musicians/"); + return response.data.map( + (musician: MusicianObj) => + new MusicianObj( + musician.id, + musician.name, + musician.bio, + musician.headshot_id, + ), + ); +}; + +export const patchMusician = async ( + id: number, + bio: string, + name: string, + headshot_id: string, + user_token: string, +): Promise => { + const response = await api.patch( + `/musicians/${id}/`, + { id, bio, name, headshot_id }, + { headers: { Authorization: `Bearer ${user_token}` } }, + ); + return new MusicianObj( + response.data.id, + response.data.name, + response.data.bio, + response.data.headshot_id, + ); +}; + +export const patchGroup = async ( + id: number, + bio: string, + name: string, + user_token: string, +): Promise => { + const response = await api.patch( + `/group/`, + { id, bio, name }, + { headers: { Authorization: `Bearer ${user_token}` } }, + ); + return new GroupObj(response.data.id, response.data.name, response.data.bio); +}; + +export const postHeadshot = async ( + id: number, + file: File, + user_token: string, +): Promise => { + const formData = new FormData(); + formData.append("file", file); + const response = await api.post(`/musicians/${id}/headshot`, formData, { + headers: { Authorization: `Bearer ${user_token}` }, + }); + return new MusicianObj( + response.data.id, + response.data.name, + response.data.bio, + response.data.headshot_id, + ); +}; + +export const postMessage = async ( + name: string, + email: string, + message: string, +): Promise => { + await api.post("/contact/", { name, email, message }); + return; +}; + +export const postSeriesPoster = async ( + series_id: number, + poster: File, + user_token: string, +): Promise => { + const formData = new FormData(); + formData.append("poster", poster); + const response = await api.post(`/events/${series_id}/poster`, formData, { + headers: { Authorization: `Bearer ${user_token}` }, + }); + return new EventSeriesObj( + response.data.series_id, + response.data.name, + response.data.description, + response.data.events.map( + (event: EventObj) => + new EventObj( + event.event_id, + event.location, + event.time, + event.ticket_url, + event.map_url, + ), + ), + response.data.poster_id, + ); +}; + +export const getSeriesList = async (): Promise => { + const response = await api.get("/events/"); + return response.data.map( + (series: EventSeriesObj) => + new EventSeriesObj( + series.series_id, + series.name, + series.description, + series.events.map( + (event: EventObj) => + new EventObj( + event.event_id, + event.location, + event.time, + event.ticket_url, + event.map_url, + ), + ), + series.poster_id, + ), + ); +}; + +export const postSeries = async ( + series: EventSeriesObj, + user_token: string, +): Promise => { + const response = await api.post("/events/", series, { + headers: { Authorization: `Bearer ${user_token}` }, + }); + return new EventSeriesObj( + response.data.series_id, + response.data.name, + response.data.description, + response.data.events.map( + (event: EventObj) => + new EventObj( + event.event_id, + event.location, + event.time, + event.ticket_url, + event.map_url, + ), + ), + response.data.poster_id, + ); +}; +export const deleteSeries = async ( + series_id: number, + user_token: string, +): Promise => { + await api.delete(`/events/${series_id}/`, { + headers: { Authorization: `Bearer ${user_token}` }, + }); + return; +}; + +export const putSeries = async ( + series: EventSeriesObj, + user_token: string, +): Promise => { + const response = await api.put(`/events/${series.series_id}/`, series, { + headers: { Authorization: `Bearer ${user_token}` }, + }); + return new EventSeriesObj( + response.data.series_id, + response.data.name, + response.data.description, + response.data.events.map( + (event: EventObj) => + new EventObj( + event.event_id, + event.location, + event.time, + event.ticket_url, + event.map_url, + ), + ), + response.data.poster_id, + ); +}; diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..35b212b --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,14 @@ +:root { + --group-info-color: rgb(124, 1, 68); + --group-info-darker-color: rgba(73, 1, 41, 0.8); + --group-info-lighter-color: rgba(206, 6, 116, 0.8); + --grapefruit-yellow: rgb(252, 171, 83); + --grapefruit-yellow-washed-out: rgb(255, 204, 150); + --group-info-disabled-color: rgba(124, 1, 68, 0.5); +} + +html, +body, +#root { + height: 100%; +} diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..bfeeddf --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,16 @@ +import "bootstrap/dist/css/bootstrap.min.css"; +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import "./index.css"; +import { GoogleOAuthProvider } from "@react-oauth/google"; + +const googleClientID = import.meta.env.VITE_GOOGLE_CLIENT_ID as string; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + , +); diff --git a/client/src/vite-env.d.ts b/client/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..b2e3e7e --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..76510a9 --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import version from "vite-plugin-package-version"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), version()], +}); diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..463eca9 --- /dev/null +++ b/server/.env.example @@ -0,0 +1,15 @@ +[mysql] +DB_HOST=localhost +DB_USER=uresname +DB_PASSWORD=password +DB_DATABASE=databasename + +[cloudinary] +CLOUDINARY_URL=cloudinary://yourkey:yoursecret@yourcloudname + +[contact] +APP_PASSWORD=gmailpassword +EMAIL=destination@example.com + +[oauth2-google] +AUDIENCE=some-value.apps.googleusercontent.com diff --git a/server/.github/workflows/build.yml b/server/.github/workflows/build.yml new file mode 100644 index 0000000..9feb959 --- /dev/null +++ b/server/.github/workflows/build.yml @@ -0,0 +1,36 @@ +name: thegrapefruitsduo backend build + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v3 + with: + python-version: "3.12" + + - name: Install poetry + uses: abatilo/actions-poetry@v2 + + - name: Install dependencies + run: | + poetry install + + - name: Lint with black + run: | + poetry run black --check . + + - name: Test with pytest + run: | + poetry run pytest -s diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..90f8fbb --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python \ No newline at end of file diff --git a/server/.python-version b/server/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/server/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..1f2d7a5 --- /dev/null +++ b/server/README.md @@ -0,0 +1,45 @@ +# Backend for The Grapefruits Duo + +This is the backend for thegrapefruitsduo.com and is deployed at api.thegrapefruitsduo.com. It is a FastAPI app that serves information about the chamber music, the group's members, and upcoming events. It allows authorized users to modify most information. Data is persisted with MariaDB. + +The general flow of this program is as follows, starting from the database layer: + +- `python-mysql` is used to interact with the MariaDB database. This happens in `app/db` +- `app/controllers` contains the business logic for the app and consumes the database layer. Each controller is responsible for a different part of the app, with one main controller `app/controllers/controller.py` which imports, instantiates, and uses the other controllers. +- `app/routes` contains the FastAPI routes that consume the single controller. This controller is instantiated in `app/controllers/__init__.py` and passed to the routes. + +No formal api specification is provided, but the routes are documented with FastAPI's Swagger UI at `/docs`. + +## Basic Usage + +Use of [poetry](https://python-poetry.org/docs/) is required. Creating the virtual environment with poetry is easy and should be done in the main project directory. `.venv` should be alongside `pyproject.toml`. + +To install dependencies (venv will be created automatically if it doesn't exist): + +```bash +poetry install +``` + +The following steps require proper environment variables to be set. An example can be found in `.env.example` + +To seed the mysql database: + +```bash +poetry run seed +``` + +To retrieve a token for testing: + +```bash +poetry run token +``` + +To run the FastAPI app in development mode: + +```bash +poetry run dev +``` + +### Deployment + +This app is deployed on a Linode Ubuntu Server instance. NGINX is used as a reverse proxy and the app itself is managed by `systemd` and `uvicorn` as a service, and listens on port 6000. The app is served over HTTPS with a Let's Encrypt certificate. diff --git a/server/app/__init__.py b/server/app/__init__.py new file mode 100644 index 0000000..7436e63 --- /dev/null +++ b/server/app/__init__.py @@ -0,0 +1 @@ +from app.main import app diff --git a/server/app/admin/__init__.py b/server/app/admin/__init__.py new file mode 100644 index 0000000..887bf64 --- /dev/null +++ b/server/app/admin/__init__.py @@ -0,0 +1,3 @@ +from fastapi.security import HTTPBearer, OAuth2PasswordBearer + +oauth2_http = HTTPBearer() diff --git a/server/app/admin/contact.py b/server/app/admin/contact.py new file mode 100644 index 0000000..3963f1f --- /dev/null +++ b/server/app/admin/contact.py @@ -0,0 +1,17 @@ +import smtplib +from email.mime.text import MIMEText +from os import getenv + +HOST = "grapefruitswebsite@gmail.com" + + +def send_email(subject: str, body: str) -> None: + password = getenv("APP_PASSWORD") + email = getenv("EMAIL") + msg = MIMEText(body) + msg["Subject"] = subject + msg["From"] = HOST + smtp_server = smtplib.SMTP_SSL("smtp.gmail.com", 465) + smtp_server.login(HOST, password) # type: ignore + smtp_server.sendmail(HOST, [email], msg.as_string()) # type: ignore + smtp_server.quit() diff --git a/server/app/admin/images.py b/server/app/admin/images.py new file mode 100644 index 0000000..336a69b --- /dev/null +++ b/server/app/admin/images.py @@ -0,0 +1,48 @@ +# Set your Cloudinary credentials +# ============================== + +from pprint import pprint + +from dotenv import load_dotenv + +load_dotenv() + + +# Import the Cloudinary libraries +# ============================== +import cloudinary +import cloudinary.api +import cloudinary.uploader + +# Set configuration parameter: return "https" URLs by setting secure=True +# ============================== +cloudinary.config(secure=True) + +uploader = cloudinary.uploader + + +class CloudinaryException(Exception): + pass + + +def delete_image(public_id: str) -> None: + result = uploader.destroy(public_id) + + if result.get("result") != "ok": + raise CloudinaryException("Failed to delete image") + + +def get_image_data(public_id: str) -> dict: + data = cloudinary.api.resource(public_id) + return data + + +def get_image_url(public_id: str) -> str: + url = cloudinary.utils.cloudinary_url(public_id)[0] + if url is None: + raise CloudinaryException("Failed to get image URL") + return url + + +if __name__ == "__main__": + image_id = "coco_copy_jywbxm" diff --git a/server/app/admin/oauth_token.py b/server/app/admin/oauth_token.py new file mode 100644 index 0000000..ccbd82d --- /dev/null +++ b/server/app/admin/oauth_token.py @@ -0,0 +1,27 @@ +from os import getenv + +from fastapi.security.http import HTTPAuthorizationCredentials +from google.auth import jwt +from icecream import ic + + +def _token_claims(token: HTTPAuthorizationCredentials) -> dict: + aud = getenv("AUDIENCE") + credentials = token.credentials + claims = jwt.decode(credentials, aud, verify=False) + if not claims: + raise ValueError("Invalid token") + if claims.get("aud") != aud: + raise ValueError("Invalid audience") + if claims.get("email_verified") is not True: + raise ValueError("Email not verified") + if not claims.get("email"): + raise ValueError("Email not found in token") + if not claims.get("sub"): + raise ValueError("Sub not found in token") + return claims + + +def email_and_sub(token: HTTPAuthorizationCredentials) -> tuple[str, str]: + claims = _token_claims(token) + return claims["email"], claims["sub"] diff --git a/server/app/controllers/__init__.py b/server/app/controllers/__init__.py new file mode 100644 index 0000000..98becfe --- /dev/null +++ b/server/app/controllers/__init__.py @@ -0,0 +1,3 @@ +from app.controllers.controller import Controller + +controller = Controller() diff --git a/server/app/controllers/base_controller.py b/server/app/controllers/base_controller.py new file mode 100644 index 0000000..10203ae --- /dev/null +++ b/server/app/controllers/base_controller.py @@ -0,0 +1,28 @@ +from fastapi import HTTPException, UploadFile, status + +from app.db.base_queries import BaseQueries + +ALLOWED_FILES_TYPES = ["image/jpeg", "image/png"] +MAX_FILE_SIZE = 1000000 # 1 MB + + +class BaseController: + def __init__(self) -> None: + self.db: BaseQueries = None # type: ignore + self.ALL_FILES = ALLOWED_FILES_TYPES + self.MAX_FILE_SIZE = MAX_FILE_SIZE + + async def verify_image(self, file: UploadFile) -> bytes: + print("verifying image") + if file.content_type not in self.ALL_FILES: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"File type {file.content_type} not allowed. Allowed file types are {self.ALL_FILES}", + ) + image_file = await file.read() + if len(image_file) > self.MAX_FILE_SIZE: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"File size {len(image_file)} bytes exceeds maximum of {self.MAX_FILE_SIZE} bytes", + ) + return image_file diff --git a/server/app/controllers/controller.py b/server/app/controllers/controller.py new file mode 100644 index 0000000..84c8ca3 --- /dev/null +++ b/server/app/controllers/controller.py @@ -0,0 +1,99 @@ +from fastapi import HTTPException, UploadFile, status +from fastapi.security import HTTPAuthorizationCredentials +from icecream import ic + +from app.admin import oauth_token +from app.controllers.events import EventController +from app.controllers.group import GroupController +from app.controllers.musicians import MusicianController +from app.controllers.users import UserController +from app.models.event import EventSeries, NewEventSeries +from app.models.group import Group +from app.models.musician import Musician +from app.models.user import User + + +class Controller: + def __init__(self) -> None: + self.event_controller = EventController() + self.musician_controller = MusicianController() + self.user_controller = UserController() + self.group_controller = GroupController() + + async def get_musicians(self) -> list[Musician]: + return await self.musician_controller.get_musicians() + + async def get_musician(self, id: int) -> Musician: + return await self.musician_controller.get_musician(id) + + async def update_musician( + self, + musician: Musician, + url_param_id: int, + token: HTTPAuthorizationCredentials, + file: UploadFile | None = None, + ) -> Musician: + + if musician.id != url_param_id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="ID in URL does not match ID in request body", + ) + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + return await self.musician_controller.update_musician( + musician_id=musician.id, + new_bio=musician.bio, + file=file, + ) + + async def get_events(self) -> list[EventSeries]: + return await self.event_controller.get_all_series() + + async def get_event(self, id: int) -> EventSeries: + return await self.event_controller.get_one_series(id) + + async def create_event( + self, series: NewEventSeries, token: HTTPAuthorizationCredentials + ) -> EventSeries: + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + return await self.event_controller.create_series(series) + + async def add_series_poster( + self, series_id: int, poster: UploadFile, token: HTTPAuthorizationCredentials + ) -> EventSeries: + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + return await self.event_controller.add_series_poster(series_id, poster) + + async def delete_series(self, id: int, token: HTTPAuthorizationCredentials) -> None: + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + await self.event_controller.delete_series(id) + + async def update_series( + self, route_id: int, series: EventSeries, token: HTTPAuthorizationCredentials + ) -> EventSeries: + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + return await self.event_controller.update_series(route_id, series) + + async def get_users(self) -> list[User]: + return await self.user_controller.get_users() + + async def get_user(self, id: int) -> User: + return await self.user_controller.get_user_by_id(id) + + async def create_user(self, token: HTTPAuthorizationCredentials) -> User: + return await self.user_controller.create_user(token) + + async def get_group(self) -> Group: + return await self.group_controller.get_group() + + async def update_group_bio( + self, bio: str, token: HTTPAuthorizationCredentials + ) -> Group: + _, sub = oauth_token.email_and_sub(token) + await self.user_controller.get_user_by_sub(sub) + return await self.group_controller.update_group_bio(bio) diff --git a/server/app/controllers/events.py b/server/app/controllers/events.py new file mode 100644 index 0000000..28e0854 --- /dev/null +++ b/server/app/controllers/events.py @@ -0,0 +1,106 @@ +from fastapi import HTTPException, UploadFile, status +from icecream import ic +from mysql.connector.errors import IntegrityError + +from app.admin.images import uploader +from app.controllers.base_controller import BaseController +from app.db import event_queries +from app.db.events import EventQueries +from app.models.event import Event, EventSeries, NewEventSeries + + +class EventController(BaseController): + def __init__(self) -> None: + super().__init__() + self.db: EventQueries = event_queries + + def _all_series(self, data: list[dict]) -> list[EventSeries]: + all_series: dict[str, EventSeries] = {} + + for event_series_row in data: + series_name: str = event_series_row["name"] + event = Event(**event_series_row) + if series_name not in all_series: + all_series[series_name] = EventSeries(**event_series_row, events=[]) + all_series[series_name].events.append(event) + + return [series for series in all_series.values()] + + async def get_all_series(self) -> list[EventSeries]: + data = await self.db.get_all() + try: + return self._all_series(data) + except Exception as e: + ic(e) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error retrieving event objects: {e}", + ) + + async def get_one_series(self, id: int) -> EventSeries: + if not (data := await self.db.get_one(id)): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Event not found" + ) + try: + event = EventSeries( + **data[0], events=[Event(**e) for e in data if e.get("event_id")] + ) + return event + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating event object: {e}", + ) + + async def create_series(self, series: NewEventSeries) -> EventSeries: + try: + inserted_id = await self.db.insert_one_series(series) + for new_event in series.events: + await self.db.insert_one_event(new_event, inserted_id) + return await self.get_one_series(inserted_id) + except IntegrityError as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Series name already exists. Each series must have a unique name.\n{e}", + ) + + async def add_series_poster(self, series_id, poster: UploadFile) -> EventSeries: + series = await self.get_one_series(series_id) + series.poster_id = await self._upload_poster(poster) + await self.db.update_series_poster(series) + return await self.get_one_series(series.series_id) + + async def _upload_poster(self, poster: UploadFile) -> str: + image_file = await self.verify_image(poster) + try: + data = uploader.upload(image_file) + return data.get("public_id") + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error uploading image: {e}", + ) + + async def delete_series(self, id: int) -> None: + series = await self.get_one_series(id) + await self.db.delete_one_series(series) + + async def update_series(self, route_id: int, series: EventSeries) -> EventSeries: + if route_id != series.series_id: + print("error") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="ID in URL does not match ID in request body", + ) + prev_series = await self.get_one_series(series.series_id) + if series.poster_id != prev_series.poster_id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Poster ID cannot be updated directly. Use the /poster endpoint instead.", + ) + await self.db.delete_events_by_series(series) + await self.db.replace_series(series) + for event in series.events: + await self.db.insert_one_event(event, series.series_id) + return await self.get_one_series(series.series_id) diff --git a/server/app/controllers/group.py b/server/app/controllers/group.py new file mode 100644 index 0000000..73ee9df --- /dev/null +++ b/server/app/controllers/group.py @@ -0,0 +1,35 @@ +from fastapi import HTTPException, status + +from app.controllers.base_controller import BaseController +from app.db import group_queries +from app.db.group import GroupQueries +from app.models.group import Group + + +class GroupController(BaseController): + def __init__(self) -> None: + super().__init__() + self.db: GroupQueries = group_queries + + async def get_group(self) -> Group: + if (data := await self.db.get_one()) is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Group not found" + ) + try: + return Group(**data) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating group object: {e}", + ) + + async def update_group_bio(self, bio: str) -> Group: + try: + await self.db.update_group_bio(bio) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error updating group bio: {e}", + ) + return await self.get_group() diff --git a/server/app/controllers/musicians.py b/server/app/controllers/musicians.py new file mode 100644 index 0000000..78f01c0 --- /dev/null +++ b/server/app/controllers/musicians.py @@ -0,0 +1,89 @@ +from fastapi import HTTPException, UploadFile, status +from icecream import ic + +from app.admin.images import uploader +from app.controllers.base_controller import BaseController +from app.db import musician_queries +from app.db.musicians import MusicianQueries +from app.models.musician import Musician + + +class MusicianController(BaseController): + def __init__(self) -> None: + super().__init__() + self.db: MusicianQueries = musician_queries + + async def get_musicians(self) -> list[Musician]: + data = await self.db.get_all() + try: + return [Musician(**m) for m in data] + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating musician objects: {e}", + ) + + async def get_musician(self, id: int) -> Musician: + if (data := await self.db.get_one(id)) is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Musician not found" + ) + try: + return Musician(**data) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating musician object: {e}", + ) + + async def update_musician( + self, + musician_id: int, + new_bio: str, + file: UploadFile | None = None, + ) -> Musician: + musician = await self.get_musician(musician_id) + if new_bio != musician.bio: + return await self.update_musician_bio(musician.id, new_bio) + if file is not None: + return await self.upload_headshot(musician.id, file) + + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Update operation not implemented. Neither the bio or headshot was updated.", + ) + + async def update_musician_headshot(self, id: int, headshot_id: str) -> Musician: + await self.get_musician(id) + try: + await self.db.update_headshot(id, headshot_id) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error updating musician headshot: {e}", + ) + return await self.get_musician(id) + + async def update_musician_bio(self, id: int, bio: str) -> Musician: + await self.get_musician(id) # Check if musician exists + try: + await self.db.update_bio(id, bio) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error updating musician bio: {e}", + ) + return await self.get_musician(id) + + async def upload_headshot(self, id: int, file: UploadFile) -> Musician: + image_file = await self.verify_image(file) + data = uploader.upload(image_file) + public_id = data.get("public_id") + if public_id is None: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to upload image", + ) + await self.update_musician_headshot(id, public_id) + + return await self.get_musician(id) diff --git a/server/app/controllers/users.py b/server/app/controllers/users.py new file mode 100644 index 0000000..cea8a8b --- /dev/null +++ b/server/app/controllers/users.py @@ -0,0 +1,70 @@ +from fastapi import HTTPException, status +from fastapi.security import HTTPAuthorizationCredentials + +from app.admin import oauth_token +from app.controllers.base_controller import BaseController +from app.db import user_queries +from app.db.users import UserQueries +from app.models.user import User + + +class UserController(BaseController): + def __init__(self) -> None: + super().__init__() + self.db: UserQueries = user_queries + + async def get_users(self) -> list[User]: + data = await self.db.get_all() + try: + return [User(**e) for e in data] + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating user objects: {e}", + ) + + async def get_user_by_id(self, id: int) -> User: + if (data := await self.db.get_one(id)) is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="User not found" + ) + try: + return User(**data) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating user object: {e}", + ) + + async def get_user_by_email(self, email: str) -> User: + if (data := await self.db.get_one_by_email(email)) is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="User does not exist" + ) + try: + return User(**data) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating user object: {e}", + ) + + async def get_user_by_sub(self, sub: str) -> User: + if (data := await self.db.get_one_by_sub(sub)) is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="User not found" + ) + try: + return User(**data) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating user object: {e}", + ) + + async def create_user(self, token: HTTPAuthorizationCredentials) -> User: + email, sub = oauth_token.email_and_sub(token) + user: User = await self.get_user_by_email(email) + if user.sub is None: + await self.db.update_sub(user.email, sub) + return await self.get_user_by_sub(sub) diff --git a/server/app/db/__init__.py b/server/app/db/__init__.py new file mode 100644 index 0000000..d3bb676 --- /dev/null +++ b/server/app/db/__init__.py @@ -0,0 +1,9 @@ +from .events import EventQueries +from .group import GroupQueries +from .musicians import MusicianQueries +from .users import UserQueries + +event_queries = EventQueries() +user_queries = UserQueries() +musician_queries = MusicianQueries() +group_queries = GroupQueries() diff --git a/server/app/db/base_queries.py b/server/app/db/base_queries.py new file mode 100644 index 0000000..6b44080 --- /dev/null +++ b/server/app/db/base_queries.py @@ -0,0 +1,32 @@ +from typing import Callable + +from app.db.conn import connect_db + + +class BaseQueries: + from icecream import ic + + def __init__(self) -> None: + self.table: str = None # type: ignore + self.connect_db: Callable = connect_db + + async def get_all(self) -> list[dict]: + query = f"SELECT * FROM {self.table}" + db = connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query) + data = cursor.fetchall() + cursor.close() + db.close() + return data # type: ignore + + async def get_one(self, id: int) -> dict | None: + query = f"SELECT * FROM {self.table} WHERE id = %s" + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query, (id,)) + data = cursor.fetchone() + cursor.close() + db.close() + + return data # type: ignore diff --git a/server/app/db/conn.py b/server/app/db/conn.py new file mode 100644 index 0000000..a3f281a --- /dev/null +++ b/server/app/db/conn.py @@ -0,0 +1,31 @@ +import os + +import mysql.connector +from dotenv import load_dotenv + + +class DBException(Exception): + pass + + +def connect_db() -> mysql.connector.MySQLConnection: + load_dotenv() + host = os.getenv("DB_HOST") + user = os.getenv("DB_USER") + password = os.getenv("DB_PASSWORD") + database = os.getenv("DB_DATABASE") + + if None in [host, user, password, database]: + raise DBException("Missing database credentials") + + try: + return mysql.connector.connect( + host=host, + user=user, + password=password, + database=database, + auth_plugin="mysql_native_password", + ) # type: ignore + + except mysql.connector.Error as err: + raise DBException("Could not connect to database") from err diff --git a/server/app/db/events.py b/server/app/db/events.py new file mode 100644 index 0000000..1c057cf --- /dev/null +++ b/server/app/db/events.py @@ -0,0 +1,140 @@ +from asyncio import gather + +from icecream import ic + +from app.db.base_queries import BaseQueries +from app.models.event import ( + EVENT_TABLE, + SERIES_TABLE, + Event, + EventSeries, + NewEvent, + NewEventSeries, +) + + +class EventQueries(BaseQueries): + def __init__(self) -> None: + super().__init__() + self.table = SERIES_TABLE + + async def get_one(self, series_id: int) -> list[dict] | None: + query = f""" + SELECT * + FROM {SERIES_TABLE} + INNER JOIN {EVENT_TABLE} + ON {SERIES_TABLE}.series_id = {EVENT_TABLE}.series_id + WHERE {SERIES_TABLE}.series_id = %s + """ + + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query, (series_id,)) + data = cursor.fetchall() + cursor.close() + db.close() + return data + + async def get_all(self) -> list[dict]: + query = f""" + SELECT * + FROM {SERIES_TABLE} + INNER JOIN {EVENT_TABLE} + ON {SERIES_TABLE}.series_id = {EVENT_TABLE}.series_id + """ + + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query) + data = cursor.fetchall() + cursor.close() + db.close() + return data + + async def insert_one_series(self, series: NewEventSeries) -> int: + query = f"INSERT INTO {self.table} (name, description) VALUES (%s, %s)" + db = self.connect_db() + cursor = db.cursor() + cursor.execute( + query, + ( + series.name, + series.description, + ), + ) + inserted_id = cursor.lastrowid + db.commit() + cursor.close() + db.close() + return inserted_id + + async def insert_one_event(self, event: NewEvent, series_id: int) -> int: + query = f"INSERT INTO {EVENT_TABLE} (series_id, location, time, ticket_url, map_url) VALUES (%s, %s, %s, %s, %s)" + db = self.connect_db() + cursor = db.cursor() + ticket_url = str(event.ticket_url) if event.ticket_url else None + map_url = str(event.map_url) if event.map_url else None + cursor.execute( + query, (series_id, event.location, event.time, ticket_url, map_url) + ) + iserted_id = cursor.lastrowid + db.commit() + cursor.close() + db.close() + return iserted_id + + async def delete_events_by_series(self, series: EventSeries) -> None: + query = f"DELETE FROM {EVENT_TABLE} WHERE series_id = %s" + db = self.connect_db() + cursor = db.cursor() + cursor.execute(query, (series.series_id,)) + db.commit() + cursor.close() + + async def delete_one_series(self, series: EventSeries) -> None: + query = f"DELETE FROM {self.table} WHERE series_id = %s" + db = self.connect_db() + cursor = db.cursor() + cursor.execute(query, (series.series_id,)) + db.commit() + cursor.close() + + async def update_series_poster(self, series: EventSeries) -> None: + query = f"UPDATE {self.table} SET poster_id = %s WHERE series_id = %s" + db = self.connect_db() + cursor = db.cursor() + cursor.execute(query, (series.poster_id, series.series_id)) + db.commit() + cursor.close() + + async def replace_event(self, event: Event) -> None: + query = f""" + UPDATE {EVENT_TABLE} + SET location = %s, time = %s, ticket_url = %s, map_url = %s + WHERE event_id = %s + """ + db = self.connect_db() + cursor = db.cursor() + ticket_url = str(event.ticket_url) if event.ticket_url else None + map_url = str(event.map_url) if event.map_url else None + cursor.execute( + query, (event.location, event.time, ticket_url, map_url, event.event_id) + ) + db.commit() + cursor.close() + db.close() + + async def replace_series(self, series: EventSeries) -> None: + query = f""" + UPDATE {self.table} + SET name = %s, description = %s, poster_id = %s + WHERE series_id = %s + """ + db = self.connect_db() + cursor = db.cursor() + cursor.execute( + query, (series.name, series.description, series.poster_id, series.series_id) + ) + db.commit() + cursor.close() + db.close() diff --git a/server/app/db/group.py b/server/app/db/group.py new file mode 100644 index 0000000..b000e44 --- /dev/null +++ b/server/app/db/group.py @@ -0,0 +1,36 @@ +from app.db.base_queries import BaseQueries +from app.models.group import GROUP_TABLE + + +class GroupQueries(BaseQueries): + def __init__(self) -> None: + super().__init__() + self.table = GROUP_TABLE + + async def get_one(self) -> dict: + query = f"SELECT * FROM {self.table}" + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query) + data = cursor.fetchone() + cursor.close() + db.close() + + if not data: + raise Exception("error retrieving group") + + return data + + async def get_all(self) -> None: + raise NotImplementedError( + "get_all method not implemented for GroupQueries. There's only one row in the table." + ) + + async def update_group_bio(self, bio: str) -> None: + db = self.connect_db() + cursor = db.cursor() + query = f"UPDATE {self.table} SET bio = %s WHERE id = 1" # only one row in the table + cursor.execute(query, (bio,)) + db.commit() + cursor.close() + db.close() diff --git a/server/app/db/musicians.py b/server/app/db/musicians.py new file mode 100644 index 0000000..bbbafff --- /dev/null +++ b/server/app/db/musicians.py @@ -0,0 +1,29 @@ +from icecream import ic + +from app.db.base_queries import BaseQueries +from app.db.conn import connect_db +from app.models.musician import MUSICIAN_TABLE + + +class MusicianQueries(BaseQueries): + def __init__(self) -> None: + super().__init__() + self.table = MUSICIAN_TABLE + + async def update_bio(self, id: int, bio: str) -> None: + db = connect_db() + cursor = db.cursor() + query = f"UPDATE {self.table} SET bio = %s WHERE id = %s" + cursor.execute(query, (bio, id)) + db.commit() + cursor.close() + db.close() + + async def update_headshot(self, id: int, headshot_id: str) -> None: + db = connect_db() + cursor = db.cursor() + query = f"UPDATE {self.table} SET headshot_id = %s WHERE id = %s" + cursor.execute(query, (headshot_id, id)) + db.commit() + cursor.close() + db.close() diff --git a/server/app/db/users.py b/server/app/db/users.py new file mode 100644 index 0000000..77401eb --- /dev/null +++ b/server/app/db/users.py @@ -0,0 +1,42 @@ +from app.db.base_queries import BaseQueries +from app.models.user import USER_TABLE + + +class UserQueries(BaseQueries): + def __init__(self) -> None: + super().__init__() + self.table = USER_TABLE + + async def get_one_by_email(self, email: str) -> dict | None: + query = f"SELECT * FROM {self.table} WHERE email = %s" + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query, (email,)) + data = cursor.fetchone() + cursor.close() + db.close() + + return data + + async def get_one_by_sub(self, sub: str) -> dict | None: + query = f"SELECT * FROM {self.table} WHERE sub = %s" + db = self.connect_db() + cursor = db.cursor(dictionary=True) + cursor.execute(query, (sub,)) + data = cursor.fetchone() + cursor.close() + db.close() + + if not data: + return None + + return data + + async def update_sub(self, email: str, sub: str) -> None: + query = f"UPDATE {self.table} SET sub = %s WHERE email = %s" + db = self.connect_db() + cursor = db.cursor() + cursor.execute(query, (sub, email)) + db.commit() + cursor.close() + db.close() diff --git a/server/app/main.py b/server/app/main.py new file mode 100644 index 0000000..92b8b15 --- /dev/null +++ b/server/app/main.py @@ -0,0 +1,56 @@ +from asyncio import gather + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from app.controllers import Controller +from app.models.tgd import TheGrapefruitsDuo +from app.routers.contact import router as contact_router +from app.routers.events import router as event_router +from app.routers.group import router as group_router +from app.routers.musicians import router as musician_router +from app.routers.users import router as user_router +from app.scripts.version import get_version + +app = FastAPI( + title="The Grapefruits Duo API", + description="API for The Grapefruits Duo website", + version=get_version(), +) +app.include_router(musician_router) +app.include_router(group_router) +app.include_router(contact_router) +app.include_router(event_router) +app.include_router(user_router) + +controller = Controller() + +origins = [ + "http://localhost:3000", + "https://thegrapefruitsduo.com", + "https://www.thegrapefruitsduo.com", + "https://tgd.lucasjensen.me", +] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/", tags=["root"]) +async def root() -> TheGrapefruitsDuo: + musicians, events, group = await gather( + controller.get_musicians(), + controller.get_events(), + controller.get_group(), + ) + return TheGrapefruitsDuo( + version=get_version(), + group=group, + musicians=musicians, + events=events, + ) diff --git a/server/app/models/__init__.py b/server/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/app/models/contact.py b/server/app/models/contact.py new file mode 100644 index 0000000..b66bd6b --- /dev/null +++ b/server/app/models/contact.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel, EmailStr + + +class Contact(BaseModel): + name: str + email: EmailStr + message: str diff --git a/server/app/models/event.py b/server/app/models/event.py new file mode 100644 index 0000000..d931c53 --- /dev/null +++ b/server/app/models/event.py @@ -0,0 +1,36 @@ +from datetime import datetime +from typing import Optional + +from fastapi import UploadFile +from pydantic import BaseModel, HttpUrl + + +class Poster(BaseModel): + file: UploadFile + + +class NewEvent(BaseModel): + location: str + time: datetime + map_url: Optional[HttpUrl] = None + ticket_url: Optional[HttpUrl] = None + + +class Event(NewEvent): + event_id: int + + +class NewEventSeries(BaseModel): + name: str + description: str + events: list[NewEvent] + + +class EventSeries(NewEventSeries): + series_id: int + events: list[Event] + poster_id: Optional[str] = None + + +SERIES_TABLE = "series" +EVENT_TABLE = "events" diff --git a/server/app/models/group.py b/server/app/models/group.py new file mode 100644 index 0000000..5b5ff1e --- /dev/null +++ b/server/app/models/group.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel + + +class Group(BaseModel): + name: str + bio: str + id: int | None = None + + +GROUP_TABLE = "group_table" diff --git a/server/app/models/musician.py b/server/app/models/musician.py new file mode 100644 index 0000000..8a10938 --- /dev/null +++ b/server/app/models/musician.py @@ -0,0 +1,16 @@ +from typing import Optional + +from pydantic import BaseModel, Field + + +class NewMusician(BaseModel): + name: str + bio: str + headshot_id: str # cloudinary id + + +class Musician(NewMusician): + id: int + + +MUSICIAN_TABLE = "musicians" diff --git a/server/app/models/tgd.py b/server/app/models/tgd.py new file mode 100644 index 0000000..0c3d571 --- /dev/null +++ b/server/app/models/tgd.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel + +from app.models.event import EventSeries +from app.models.group import Group +from app.models.musician import Musician + + +class TheGrapefruitsDuo(BaseModel): + version: str + group: Group + musicians: list[Musician] + events: list[EventSeries] diff --git a/server/app/models/user.py b/server/app/models/user.py new file mode 100644 index 0000000..3aa8bed --- /dev/null +++ b/server/app/models/user.py @@ -0,0 +1,13 @@ +from typing import Optional + +from pydantic import BaseModel + + +class User(BaseModel): + name: str + email: str + sub: Optional[str] = None + id: int | None = None + + +USER_TABLE = "users" diff --git a/server/app/routers/__init__.py b/server/app/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/app/routers/contact.py b/server/app/routers/contact.py new file mode 100644 index 0000000..4e5cd6b --- /dev/null +++ b/server/app/routers/contact.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, status + +from app.admin.contact import send_email +from app.models.contact import Contact + +router = APIRouter( + prefix="/contact", + tags=["contact"], + responses={404: {"description": "Not found"}}, +) + + +@router.post("/", status_code=status.HTTP_201_CREATED) +async def post_message(contact: Contact): + """Sends an email to the site owner with the provided name, email, and message.""" + subject = f"New message from {contact.name}" + body = f"From: {contact.email}\n\n{contact.message}" + send_email(subject, body) diff --git a/server/app/routers/events.py b/server/app/routers/events.py new file mode 100644 index 0000000..80ab053 --- /dev/null +++ b/server/app/routers/events.py @@ -0,0 +1,56 @@ +from fastapi import APIRouter, Depends, File, UploadFile +from fastapi.security import HTTPAuthorizationCredentials +from icecream import ic + +from app.admin import oauth2_http +from app.controllers import controller +from app.models.event import EventSeries, NewEventSeries + +router = APIRouter( + prefix="/events", + tags=["events"], + responses={404: {"description": "Not found"}}, +) + + +@router.get("/") +async def get_events() -> list[EventSeries]: + return await controller.get_events() + + +@router.get("/{id}") +async def get_event(id: int) -> EventSeries: + return await controller.get_event(id) + + +@router.post("/") +async def create_series( + series: NewEventSeries, + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> EventSeries: + return await controller.create_event(series, token) + + +@router.delete("/{id}") +async def delete_event( + id: int, token: HTTPAuthorizationCredentials = Depends(oauth2_http) +) -> None: + await controller.delete_series(id, token) + + +@router.post("/{id}/poster") +async def add_series_poster( + id: int, + poster: UploadFile = File(...), + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> EventSeries: + return await controller.add_series_poster(id, poster, token) + + +@router.put("/{id}") +async def update_event( + id: int, + event: EventSeries, + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> EventSeries: + return await controller.update_series(id, event, token) diff --git a/server/app/routers/group.py b/server/app/routers/group.py new file mode 100644 index 0000000..ddd6fe0 --- /dev/null +++ b/server/app/routers/group.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, Depends, status +from fastapi.security.http import HTTPAuthorizationCredentials +from icecream import ic + +from app.admin import oauth2_http +from app.controllers import controller +from app.models.group import Group + +router = APIRouter( + prefix="/group", + tags=["group"], + responses={404: {"description": "Not found"}}, +) + + +@router.get("/", status_code=status.HTTP_200_OK) +async def get_group() -> Group: + return await controller.get_group() + + +@router.patch("/") +async def update_group( + group: Group, token: HTTPAuthorizationCredentials = Depends(oauth2_http) +) -> Group: + """Updates the group bio, but requires the entire group object to be sent in the request body. + Requires authentication.""" + return await controller.update_group_bio(group.bio, token) diff --git a/server/app/routers/musicians.py b/server/app/routers/musicians.py new file mode 100644 index 0000000..124c03b --- /dev/null +++ b/server/app/routers/musicians.py @@ -0,0 +1,49 @@ +from fastapi import APIRouter, Depends, UploadFile, status +from fastapi.security import HTTPAuthorizationCredentials +from icecream import ic + +from app.admin import oauth2_http +from app.controllers import controller +from app.models.musician import Musician + +router = APIRouter( + prefix="/musicians", + tags=["musicians"], + responses={404: {"description": "Not found"}}, +) + + +@router.get("/", status_code=status.HTTP_200_OK) +async def get_musicians() -> list[Musician]: + return await controller.get_musicians() + + +@router.get("/{id}", status_code=status.HTTP_200_OK) +async def get_musician(id: int) -> Musician: + return await controller.get_musician(id) + + +@router.patch("/{id}") +async def update_musician( + id: int, + musician: Musician, + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> Musician: + """Updates a musician's bio, but requires the entire musician object to be sent in the request body. + Requires authentication.""" + return await controller.update_musician( + musician=musician, url_param_id=id, token=token + ) + + +@router.post("/{id}/headshot", status_code=status.HTTP_200_OK) +async def update_musician_headshot( + id: int, + file: UploadFile, + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> Musician | None: + """Recieves a headshot image file, uploads it to cloudinary, and updates the musician's headshot url in the database""" + musician = await controller.get_musician(id) + return await controller.update_musician( + musician=musician, url_param_id=id, token=token, file=file + ) diff --git a/server/app/routers/users.py b/server/app/routers/users.py new file mode 100644 index 0000000..c2c1dc6 --- /dev/null +++ b/server/app/routers/users.py @@ -0,0 +1,29 @@ +from fastapi import APIRouter, Depends, status +from fastapi.security import HTTPAuthorizationCredentials + +from app.admin import oauth2_http +from app.controllers import controller +from app.models.user import User + +router = APIRouter( + prefix="/users", + tags=["users"], + responses={404: {"description": "Not found"}}, +) + + +@router.get("/", status_code=status.HTTP_200_OK) +async def get_users() -> list[User]: + return await controller.get_users() + + +@router.get("/{id}", status_code=status.HTTP_200_OK) +async def get_user(id: int) -> User: + return await controller.get_user(id) + + +@router.post("/", status_code=status.HTTP_200_OK) +async def create_user( + token: HTTPAuthorizationCredentials = Depends(oauth2_http), +) -> User | None: + return await controller.create_user(token) diff --git a/server/app/scripts/DDL.sql b/server/app/scripts/DDL.sql new file mode 100644 index 0000000..a9f09d3 --- /dev/null +++ b/server/app/scripts/DDL.sql @@ -0,0 +1,62 @@ +/* + This file is autogenerated by DBeaver. + Paste updated definitions below as changes are made to the database schema. +*/ + +-- thegrapefruitsduo.group_table definition + +CREATE TABLE `group_table` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `bio` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +-- thegrapefruitsduo.musicians definition + +CREATE TABLE `musicians` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `bio` text NOT NULL, + `headshot_id` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +-- thegrapefruitsduo.series definition + +CREATE TABLE `series` ( + `series_id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `poster_id` varchar(255) DEFAULT NULL, + PRIMARY KEY (`series_id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +-- thegrapefruitsduo.users definition + +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `email` varchar(255) NOT NULL, + `sub` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +-- thegrapefruitsduo.events definition + +CREATE TABLE `events` ( + `event_id` int(11) NOT NULL AUTO_INCREMENT, + `series_id` int(11) NOT NULL, + `location` varchar(255) NOT NULL, + `time` datetime NOT NULL, + `ticket_url` varchar(255) DEFAULT NULL, + `map_url` varchar(255) DEFAULT NULL, + PRIMARY KEY (`event_id`), + KEY `series_id` (`series_id`), + CONSTRAINT `events_ibfk_1` FOREIGN KEY (`series_id`) REFERENCES `series` (`series_id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/server/app/scripts/__init__.py b/server/app/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/app/scripts/run.py b/server/app/scripts/run.py new file mode 100644 index 0000000..ead7cde --- /dev/null +++ b/server/app/scripts/run.py @@ -0,0 +1,11 @@ +import subprocess +from pathlib import Path + + +def main() -> None: + curr_dir = Path(__file__).resolve().parent.absolute() + script = curr_dir / "run.sh" + try: + subprocess.run(["sh", script], check=True) + except KeyboardInterrupt: + return diff --git a/server/app/scripts/run.sh b/server/app/scripts/run.sh new file mode 100755 index 0000000..ef7164b --- /dev/null +++ b/server/app/scripts/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +uvicorn app:app --reload --port 8000 \ No newline at end of file diff --git a/server/app/scripts/seed.py b/server/app/scripts/seed.py new file mode 100644 index 0000000..af77044 --- /dev/null +++ b/server/app/scripts/seed.py @@ -0,0 +1,266 @@ +from datetime import datetime + +from dotenv import load_dotenv + +from app.db.conn import connect_db +from app.models.event import EVENT_TABLE, SERIES_TABLE, Event, EventSeries +from app.models.group import GROUP_TABLE, Group +from app.models.musician import MUSICIAN_TABLE, NewMusician +from app.models.user import USER_TABLE, User + +margarite: NewMusician = NewMusician( + name="Margarite Waddell", + bio="French hornist Margarite Waddell holds positions with the Eugene Symphony, Sarasota Opera, Boise Philharmonic, Rogue Valley Symphony, and Newport Symphony. As a freelancer, Margarite has played with ensembles throughout the West Coast including the Oregon Symphony, Portland Opera, Santa Rosa Symphony, Marin Symphony, and Symphony San Jose. She has performed with popular artists such as The Who, Josh Groban, and Sarah Brightman. Margarite can be heard on Kamyar Mohajer’s album “Pictures of the Hidden” on Navona Records. She appeared as a soloist with the Silicon Valley Philharmonic in 2016. Margarite cares deeply about music education and has taught private lessons, sectionals, and masterclasses throughout the Bay Area, Southwestern Oregon, Eugene, and Corvallis since 2013. She also performed in the San Francisco Symphony's Adventures in Music program for the 2016-2017 season. Margarite received her bachelor’s degree from the University of Oregon, and her master’s degree from the San Francisco Conservatory of Music.", + headshot_id="zlpkcrvbdsicgj7qtslx", +) + +coco: NewMusician = NewMusician( + name="Coco Bender", + bio="Coco Bender is a pianist residing in the Pacific Northwest. She recently performed with Cascadia Composers, recorded original film scores by Portland composer Christina Rusnak for the Pioneers: First Woman Filmmakers Project, and during the pandemic presented a series of outdoor recitals featuring music by H. Leslie Adams, William Grant Still, Bartok, and others. Coco is a founding member of the Eugene based horn and piano duo, The Grapefruits, as well as a co-artistic director and musical director of an all-women circus, Girl Circus. She has taken master classes with Inna Faliks, Tamara Stefanovich, and Dr. William Chapman Nyaho. Coco currently studies with Dr. Thomas Otten. In addition to performing regularly, she teaches a large studio of students in the Pacific Northwest, from Seattle WA to Eugene OR. Coco was the accompanist for Portland treble choir Aurora Chorus, during their 2021-2022, season under the conductorship of Kathleen Hollingsworth, Margaret Green, Betty Busch, and Joan Szymko.", + headshot_id="coco_copy_jywbxm", +) + +coco_user: User = User( + name="Coco Bender", + email="cocobender.piano@gmail.com", +) + +margarite_user: User = User( + name="Margarite Waddell", + email="mgwaddell@gmail.com", +) + +lucas_user: User = User(name="Lucas Jensen", email="lucas.p.jensen10@gmail.com") + +tgd_user: User = User( + name="The Grapefruits Duo", + email="thegrapefruitsduo@gmail.com", +) + +tgd_website: User = User( + name="The Grapefruits Duo Website", email="grapefruitswebsite@gmail.com" +) + + +tgd: Group = Group( + bio="The Grapefruits, comprising of Coco Bender, piano, and Margarite Waddell, french horn, are a contemporary classical music duo. They perform frequently through out the PNW with the goal presenting traditional classical french horn repertoire, new 20th century works, and commissioned works by PNW composers.", + name="The Grapefruits Duo", +) + + +series1 = EventSeries( + name="The Grapefruits Duo Presents: Works for Horn and Piano", + description="Pieces by Danzi, Gomez, Gounod, Grant, and Rusnak!", + poster_id="The_Grapefruits_Present_qhng6y", + events=[ + Event( + location="Medford, OR", + time=datetime(2024, 5, 31, 19), + event_id=0, + ), + Event( + location="First Presbyterian Church Newport", + time=datetime(2024, 6, 16, 16), + event_id=0, + map_url="https://maps.app.goo.gl/hNfN8X5FBZLg8LDF8", # type: ignore + ), + Event( + location="First Church of Christ, Scientist, Eugene", + time=datetime(2024, 6, 23, 15), + event_id=0, + ), + ], + series_id=0, +) + +# series2 = EventSeries( +# name="The Grapefruits Duo Features: Solos for Bass Trombone", +# description="Pieces by Ewazen, Bozza, and more!", +# events=[ +# Event( +# location="Eugene Family YMCA", +# time=datetime(2024, 7, 1, 17, 30), +# event_id=0, +# ), +# Event( +# location="Tobi's Crate", +# time=datetime(2024, 7, 2, 20), +# event_id=0, +# ticket_url="http://www.example.com", # type: ignore +# ), +# ], +# id=0, +# ) + + +def seed(): + confirmation = input( + "Are you sure you want to seed the database? Data will be lost. [Y/n]: " + ) + if confirmation.lower() not in ["y", "yes", ""]: + print("Exiting") + return + print("Seeding database") + add_musicians() + add_users() + add_group() + add_events() + + +def add_group(): + print("Adding group") + db = connect_db() + cursor = db.cursor() + cursor.execute( + f"DROP TABLE IF EXISTS {GROUP_TABLE};", + ) + cursor.execute( + f""" + CREATE TABLE {GROUP_TABLE} ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + bio TEXT NOT NULL, + PRIMARY KEY (id) + ); + """ + ) + cursor.execute( + f"INSERT INTO {GROUP_TABLE} (name, bio) VALUES (%s, %s);", + (tgd.name, tgd.bio), + ) + db.commit() + cursor.close() + + +def add_users(): + print("Adding users") + db = connect_db() + cursor = db.cursor() + cursor.execute( + f"DROP TABLE IF EXISTS {USER_TABLE};", + ) + cursor.execute( + f""" + CREATE TABLE {USER_TABLE} ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + sub VARCHAR(255), + PRIMARY KEY (id) + ); + """ + ) + for u in [coco_user, margarite_user, lucas_user, tgd_user, tgd_website]: + cursor.execute( + f"INSERT INTO {USER_TABLE} (name, email, sub) VALUES (%s, %s, %s);", + (u.name, u.email, u.sub), + ) + + db.commit() + cursor.close() + + +def add_musicians(): + print("Adding musicians") + + db = connect_db() + cursor = db.cursor() + cursor.execute( + f"DROP TABLE IF EXISTS {MUSICIAN_TABLE};", + ) + cursor.execute( + f""" + CREATE TABLE {MUSICIAN_TABLE} ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + bio TEXT NOT NULL, + headshot_id VARCHAR(255) NOT NULL, + PRIMARY KEY (id) + ); + """ + ) + for m in [margarite, coco]: + cursor.execute( + f"INSERT INTO {MUSICIAN_TABLE} (name, bio, headshot_id) VALUES (%s, %s, %s);", + (m.name, m.bio, m.headshot_id), + ) + + db.commit() + cursor.close() + + +def add_events(): + print("Adding events") + + db = connect_db() + cursor = db.cursor() + cursor.execute( + f"DROP TABLE IF EXISTS {EVENT_TABLE};", + ) + cursor.execute( + f"DROP TABLE IF EXISTS {SERIES_TABLE};", + ) + cursor.execute( + f""" + CREATE TABLE {SERIES_TABLE} ( + series_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL UNIQUE, + description TEXT NOT NULL, + poster_id VARCHAR(255), + PRIMARY KEY (series_id) + ); + """ + ) + cursor.execute( + f""" + CREATE TABLE {EVENT_TABLE} ( + event_id INT NOT NULL AUTO_INCREMENT, + series_id INT NOT NULL, + location VARCHAR(255) NOT NULL, + time DATETIME NOT NULL, + ticket_url VARCHAR(255), + map_url VARCHAR(255), + PRIMARY KEY (event_id), + FOREIGN KEY (series_id) REFERENCES {SERIES_TABLE}(series_id) ON DELETE CASCADE + ); + """ + ) + + for series in [series1]: + cursor.execute( + f"INSERT INTO {SERIES_TABLE} (name, description, poster_id) VALUES (%s, %s, %s);", + ( + series.name, + series.description, + series.poster_id, + ), + ) + series_id = cursor.lastrowid + if series_id is None: + raise Exception("Error inserting series: could not get last row id.") + series.series_id = series_id + for event in series.events: + ticket_url = str(event.ticket_url) if event.ticket_url else None + map_url = str(event.map_url) if event.map_url else None + cursor.execute( + f"INSERT INTO {EVENT_TABLE} (series_id, location, time, ticket_url, map_url) VALUES (%s, %s, %s, %s, %s);", + ( + series.series_id, + event.location, + event.time, + ticket_url, + map_url, + ), + ) + + db.commit() + cursor.close() + + +def main(): + load_dotenv() + seed() + + +if __name__ == "__main__": + main() diff --git a/server/app/scripts/version.py b/server/app/scripts/version.py new file mode 100644 index 0000000..9bcbca6 --- /dev/null +++ b/server/app/scripts/version.py @@ -0,0 +1,18 @@ +import toml +from pathlib import Path + + +def get_version() -> str: + abs_path = Path(__file__).resolve().parent.parent.parent / "pyproject.toml" + try: + with open(abs_path) as file: + data = toml.load(file) + return data["tool"]["poetry"]["version"] + except Exception as e: + return "0.0.0" + finally: + file.close() + + +if __name__ == "__main__": + print(get_version()) diff --git a/server/poetry.lock b/server/poetry.lock new file mode 100644 index 0000000..384a746 --- /dev/null +++ b/server/poetry.lock @@ -0,0 +1,1390 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "black" +version = "24.3.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, + {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, + {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, + {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, + {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, + {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, + {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, + {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, + {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, + {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, + {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, + {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, + {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, + {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, + {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, + {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, + {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, + {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, + {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, + {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, + {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, + {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudinary" +version = "1.39.1" +description = "Python and Django SDK for Cloudinary" +optional = false +python-versions = "*" +files = [ + {file = "cloudinary-1.39.1.tar.gz", hash = "sha256:922467259045ffdd81713b5ed2bc48f1d70f7f3e9812b8b0c206e99d665ed161"}, +] + +[package.dependencies] +certifi = "*" +six = "*" +urllib3 = ">=1.26.5" + +[package.extras] +dev = ["tox"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastapi" +version = "0.110.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, + {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, +] + +[package.dependencies] +email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +httpx = {version = ">=0.23.0", optional = true, markers = "extra == \"all\""} +itsdangerous = {version = ">=1.1.0", optional = true, markers = "extra == \"all\""} +jinja2 = {version = ">=2.11.2", optional = true, markers = "extra == \"all\""} +orjson = {version = ">=3.2.1", optional = true, markers = "extra == \"all\""} +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +pydantic-extra-types = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +pydantic-settings = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +python-multipart = {version = ">=0.0.7", optional = true, markers = "extra == \"all\""} +pyyaml = {version = ">=5.3.1", optional = true, markers = "extra == \"all\""} +starlette = ">=0.36.3,<0.37.0" +typing-extensions = ">=4.8.0" +ujson = {version = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0", optional = true, markers = "extra == \"all\""} +uvicorn = {version = ">=0.12.0", extras = ["standard"], optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "google-auth" +version = "2.29.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "icecream" +version = "2.1.3" +description = "Never use print() to debug again; inspect variables, expressions, and program execution with a single, simple function call." +optional = false +python-versions = "*" +files = [ + {file = "icecream-2.1.3-py2.py3-none-any.whl", hash = "sha256:757aec31ad4488b949bc4f499d18e6e5973c40cc4d4fc607229e78cfaec94c34"}, + {file = "icecream-2.1.3.tar.gz", hash = "sha256:0aa4a7c3374ec36153a1d08f81e3080e83d8ac1eefd97d2f4fe9544e8f9b49de"}, +] + +[package.dependencies] +asttokens = ">=2.0.1" +colorama = ">=0.3.9" +executing = ">=0.3.1" +pygments = ">=2.2.0" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "mysql-connector-python" +version = "8.3.0" +description = "MySQL driver written in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mysql-connector-python-8.3.0.tar.gz", hash = "sha256:e4ff23aa8036b4c5b6463fa81398bb5a528a29f99955de6ba937f0bba57a2fe3"}, + {file = "mysql_connector_python-8.3.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:f4ee7e07cca6b744874d60d6b0b24817d9246eb4e8d7269b7ddbe68763a0bd13"}, + {file = "mysql_connector_python-8.3.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:5718e426cf67f041772d4984f709052201883f74190ba6feaddce5cbd3b99e6f"}, + {file = "mysql_connector_python-8.3.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:0deb38f05057e12af091a48e03a1ff00e213945880000f802879fae5665e7502"}, + {file = "mysql_connector_python-8.3.0-cp310-cp310-manylinux_2_17_x86_64.whl", hash = "sha256:4be4165e4cd5acb4659261ddc74e9164d2dfa0d795d5695d52f2bf39ea0762fa"}, + {file = "mysql_connector_python-8.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:51d97bf771519829797556718d81e8b9bdcd0a00427740ca57c085094c8bde17"}, + {file = "mysql_connector_python-8.3.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:5e2c86c60be08c71bae755d811fe8b89ec4feb8117ec3440ebc6c042dd6f06bc"}, + {file = "mysql_connector_python-8.3.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:de74055944b214bff56e1752ec213d705c421414c67a250fb695af0c5c214135"}, + {file = "mysql_connector_python-8.3.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b2901391b651d60dab3cc8985df94976fc1ea59fa7324c5b19d0a4177914c8dd"}, + {file = "mysql_connector_python-8.3.0-cp311-cp311-manylinux_2_17_x86_64.whl", hash = "sha256:55cb57d8098c721abce20fdef23232663977c0e5c87a4d0f9f73466f32c7d168"}, + {file = "mysql_connector_python-8.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:201e609159b84a247be87b76f5deb79e8c6b368e91f043790e62077f13f3fed8"}, + {file = "mysql_connector_python-8.3.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:c57d02fd6c28be444487e7905ede09e3fecb18377cf82908ca262826369d3401"}, + {file = "mysql_connector_python-8.3.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:9302d774025e76a0fac46bfeea8854b3d6819715a6a16ff23bfcda04218a76b7"}, + {file = "mysql_connector_python-8.3.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:85fa878fdd6accaeb7d609bd2637c2cfa61592e7f9bdbdc0da18b2fa998d3d5a"}, + {file = "mysql_connector_python-8.3.0-cp312-cp312-manylinux_2_17_x86_64.whl", hash = "sha256:de0f2f2baa9e091ca8bdc4a091f874f9cd0b84b256389596adb0e032a05fe9f9"}, + {file = "mysql_connector_python-8.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:27f8be2087627366a44a6831ec68b568c98dbf0f4ceff24682d90c21db6e0f1f"}, + {file = "mysql_connector_python-8.3.0-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:ec6dc3434a7deef74ab04e8978f6c5e181866a5423006c1b5aec5390a189d28d"}, + {file = "mysql_connector_python-8.3.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:73ee8bc5f9626c42b37342a91a825cddb3461f6bfbbd6524d8ccfd3293aaa088"}, + {file = "mysql_connector_python-8.3.0-cp38-cp38-manylinux_2_17_x86_64.whl", hash = "sha256:1db5b48b4ff7d24344217ed2418b162c7677eec86ab9766dc0e5feae39c90974"}, + {file = "mysql_connector_python-8.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:77bae496566d3da77bb0e938d89243103d20ee41633f626a47785470451bf45c"}, + {file = "mysql_connector_python-8.3.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:f7acacdf9fd4260702f360c00952ad9a9cc73e8b7475e0d0c973c085a3dd7b7d"}, + {file = "mysql_connector_python-8.3.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:5f707a9b040ad4700fc447ba955c78b08f2dd5affde37ac2401918f7b6daaba3"}, + {file = "mysql_connector_python-8.3.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:125714c998a697592bc56cce918a1acc58fadc510a7f588dbef3e53a1920e086"}, + {file = "mysql_connector_python-8.3.0-cp39-cp39-manylinux_2_17_x86_64.whl", hash = "sha256:7f4f5fa844c19ee3a78c4606f6e138b06829e75469592d90246a290c7befc322"}, + {file = "mysql_connector_python-8.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:de5c3ee89d9276356f93df003949d3ba4c486f32fec9ec9fd7bc0caab124d89c"}, + {file = "mysql_connector_python-8.3.0-py2.py3-none-any.whl", hash = "sha256:e868ccc7ad9fbc242546db04673d89cee87d12b8139affd114524553df4e5d6a"}, +] + +[package.extras] +dns-srv = ["dnspython (>=1.16.0,<=2.3.0)"] +fido2 = ["fido2 (==1.1.2)"] +gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] +opentelemetry = ["Deprecated (>=1.2.6)", "typing-extensions (>=3.7.4)", "zipp (>=0.5)"] + +[[package]] +name = "orjson" +version = "3.10.0" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c90681333619d78360d13840c7235fdaf01b2b129cb3a4f1647783b1971542b6"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:400c5b7c4222cb27b5059adf1fb12302eebcabf1978f33d0824aa5277ca899bd"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dcb32e949eae80fb335e63b90e5808b4b0f64e31476b3777707416b41682db5"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7d507c7493252c0a0264b5cc7e20fa2f8622b8a83b04d819b5ce32c97cf57b"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e286a51def6626f1e0cc134ba2067dcf14f7f4b9550f6dd4535fd9d79000040b"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8acd4b82a5f3a3ec8b1dc83452941d22b4711964c34727eb1e65449eead353ca"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:30707e646080dd3c791f22ce7e4a2fc2438765408547c10510f1f690bd336217"}, + {file = "orjson-3.10.0-cp310-none-win32.whl", hash = "sha256:115498c4ad34188dcb73464e8dc80e490a3e5e88a925907b6fedcf20e545001a"}, + {file = "orjson-3.10.0-cp310-none-win_amd64.whl", hash = "sha256:6735dd4a5a7b6df00a87d1d7a02b84b54d215fb7adac50dd24da5997ffb4798d"}, + {file = "orjson-3.10.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9587053e0cefc284e4d1cd113c34468b7d3f17666d22b185ea654f0775316a26"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bef1050b1bdc9ea6c0d08468e3e61c9386723633b397e50b82fda37b3563d72"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d16c6963ddf3b28c0d461641517cd312ad6b3cf303d8b87d5ef3fa59d6844337"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4251964db47ef090c462a2d909f16c7c7d5fe68e341dabce6702879ec26d1134"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73bbbdc43d520204d9ef0817ac03fa49c103c7f9ea94f410d2950755be2c349c"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:414e5293b82373606acf0d66313aecb52d9c8c2404b1900683eb32c3d042dbd7"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed5bb09877dc27ed0d37f037ddef6cb76d19aa34b108db270d27d3d2ef747"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5127478260db640323cea131ee88541cb1a9fbce051f0b22fa2f0892f44da302"}, + {file = "orjson-3.10.0-cp311-none-win32.whl", hash = "sha256:b98345529bafe3c06c09996b303fc0a21961820d634409b8639bc16bd4f21b63"}, + {file = "orjson-3.10.0-cp311-none-win_amd64.whl", hash = "sha256:658ca5cee3379dd3d37dbacd43d42c1b4feee99a29d847ef27a1cb18abdfb23f"}, + {file = "orjson-3.10.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4329c1d24fd130ee377e32a72dc54a3c251e6706fccd9a2ecb91b3606fddd998"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef0f19fdfb6553342b1882f438afd53c7cb7aea57894c4490c43e4431739c700"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4f60db24161534764277f798ef53b9d3063092f6d23f8f962b4a97edfa997a0"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1de3fd5c7b208d836f8ecb4526995f0d5877153a4f6f12f3e9bf11e49357de98"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f93e33f67729d460a177ba285002035d3f11425ed3cebac5f6ded4ef36b28344"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:237ba922aef472761acd697eef77fef4831ab769a42e83c04ac91e9f9e08fa0e"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98c1bfc6a9bec52bc8f0ab9b86cc0874b0299fccef3562b793c1576cf3abb570"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30d795a24be16c03dca0c35ca8f9c8eaaa51e3342f2c162d327bd0225118794a"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade1e21dfde1d37feee8cf6464c20a2f41fa46c8bcd5251e761903e46102dc6b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23c12bb4ced1c3308eff7ba5c63ef8f0edb3e4c43c026440247dd6c1c61cea4b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2d014cf8d4dc9f03fc9f870de191a49a03b1bcda51f2a957943fb9fafe55aac"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eadecaa16d9783affca33597781328e4981b048615c2ddc31c47a51b833d6319"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd583341218826f48bd7c6ebf3310b4126216920853cbc471e8dbeaf07b0b80e"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:90bfc137c75c31d32308fd61951d424424426ddc39a40e367704661a9ee97095"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13b5d3c795b09a466ec9fcf0bd3ad7b85467d91a60113885df7b8d639a9d374b"}, + {file = "orjson-3.10.0-cp38-none-win32.whl", hash = "sha256:5d42768db6f2ce0162544845facb7c081e9364a5eb6d2ef06cd17f6050b048d8"}, + {file = "orjson-3.10.0-cp38-none-win_amd64.whl", hash = "sha256:33e6655a2542195d6fd9f850b428926559dee382f7a862dae92ca97fea03a5ad"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1897aa25a944cec774ce4a0e1c8e98fb50523e97366c637b7d0cddabc42e6643"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bf565a69e0082ea348c5657401acec3cbbb31564d89afebaee884614fba36b4"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6ebc17cfbbf741f5c1a888d1854354536f63d84bee537c9a7c0335791bb9009"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2817877d0b69f78f146ab305c5975d0618df41acf8811249ee64231f5953fee"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d017863ec8aa4589be30a328dacd13c2dc49de1c170bc8d8c8a98ece0f2925"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:22c2f7e377ac757bd3476ecb7480c8ed79d98ef89648f0176deb1da5cd014eb7"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e62ba42bfe64c60c1bc84799944f80704e996592c6b9e14789c8e2a303279912"}, + {file = "orjson-3.10.0-cp39-none-win32.whl", hash = "sha256:60c0b1bdbccd959ebd1575bd0147bd5e10fc76f26216188be4a36b691c937077"}, + {file = "orjson-3.10.0-cp39-none-win_amd64.whl", hash = "sha256:175a41500ebb2fdf320bf78e8b9a75a1279525b62ba400b2b2444e274c2c8bee"}, + {file = "orjson-3.10.0.tar.gz", hash = "sha256:ba4d8cac5f2e2cff36bea6b6481cdb92b38c202bcec603d6f5ff91960595a1ed"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pyasn1" +version = "0.6.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.7.0" + +[[package]] +name = "pydantic" +version = "2.6.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.16.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-extra-types" +version = "2.6.0" +description = "Extra Pydantic types." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_extra_types-2.6.0-py3-none-any.whl", hash = "sha256:d291d521c2e2bf2e6f11971caf8d639518124ae26a76d2e712599e98c4ef2b2b"}, + {file = "pydantic_extra_types-2.6.0.tar.gz", hash = "sha256:e9a93cfb245158462acb76621785219f80ad112303a0a7784d2ada65e6ed6cba"}, +] + +[package.dependencies] +pydantic = ">=2.5.2" + +[package.extras] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] + +[[package]] +name = "pydantic-settings" +version = "2.2.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyperclip" +version = "1.8.2" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +files = [ + {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.6" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.36.3" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, + {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.29.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "7f4292c11c18ec12ef01b977eabf4677a401d4e26dab9c7c997d2a6365fb5449" diff --git a/server/pyproject.toml b/server/pyproject.toml new file mode 100644 index 0000000..18c4ccd --- /dev/null +++ b/server/pyproject.toml @@ -0,0 +1,37 @@ +[tool.poetry] +name = "thegrapefruitsduo" +version = "0.3.1" +package-mode = false +description = "FastAPI backend for thegrapefruitsduo.com" +authors = ["Lucas Jensen "] +readme = "README.md" +packages = [{ include = "app" }] + + +[tool.poetry.dependencies] +python = "^3.12" +fastapi = { extras = ["all"], version = "^0.110.0" } +python-dotenv = "^1.0.1" +icecream = "^2.1.3" +mysql-connector-python = "^8.3.0" +cloudinary = "^1.39.1" +toml = "^0.10.2" +pyperclip = "^1.8.2" +google-auth = "^2.29.0" + + +[tool.poetry.dev-dependencies] +black = "^24.3.0" +pytest-asyncio = "^0.23.6" +pytest = "^8.1.1" + + +[tool.poetry.scripts] +dev = "app.scripts.run:main" +seed = "app.scripts.seed:main" +token = "app.scripts.token:main" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/server/tests/__init__.py b/server/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/tests/test_app.py b/server/tests/test_app.py new file mode 100644 index 0000000..e8347c5 --- /dev/null +++ b/server/tests/test_app.py @@ -0,0 +1,9 @@ +from fastapi.testclient import TestClient + +from app import app + +client = TestClient(app=app) + + +def test_app(): + assert isinstance(client, TestClient)