diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ace6820 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ + +# +# This environment file sets variables for the development +# Database specifics are set in the docker-compose file and the ORIGIN is not required for local development +# + +# prisma database url +DATABASE_URL="postgresql://prisma:prisma@localhost:5432/finances?schema=public" diff --git a/.gitignore b/.gitignore index 4ce7d2f..fdc69e7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ next-env.d.ts # IDE /.idea + +# serwist +public/sw.js diff --git a/README.md b/README.md index 1539d89..9f22666 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,70 @@ # Next-Finances This is my simple finances tracker that I use to keep track of my spending. + +## Using the app + +### Understanding the Basics + +- **Entities**: The core building blocks of your finances. + - Accounts: Where you hold money (e.g., bank accounts, PayPal account, cash) + - Entities: Where you spend money (e.g., Walmart, Spotify, Netflix) +- **Payments**: Record money movement. + - Expenses: Money leaving an Account. (Account -> Entity) + - Income: Money entering an Account. (Entity -> Account) +- **Categories** *(optional)*: Add labels to Payments for better tracking. + +### Your First Steps + +- Set up: Create Entities and Accounts that reflect your finances. +- Record a Payment: + - Enter the amount and date. + - Select payor and payee + - *(optional)* Assign a category or enter a note. +- Explore: View your payment history and view your statics at the dashboard + +### Tips + +- Install the website as a PWA for easy access. +- Get in the habit of recording Payments as they happen for accurate tracking. +- Use categories to understand your spending patterns. + +## Development + +Clone this repository and run the following commands: + +```bash + +## create .env file +cp .env.example .env + +## start the database +docker compose -f docker/finances-dev/docker-compose.yml up -d + +## generate prisma client +npx prisma generate + +## apply database migrations +npx prisma migrate deploy + +## start the development server +npm run dev + +``` + +Then open [http://localhost:3000](http://localhost:3000) with your browser and create an account. +While in development mode, you can generate sample data from the [Account page](http://localhost:3000/account). + +## Deployment + +Copy the [docker-compose.yaml](./docker/finances-prod/docker-compose.yaml) file and +the [.env.example](./docker/finances-prod/.env.example) from 'docker/finances-prod' to your server. + +Rename the `.env.example` file to `.env` and adjust the required environment variables. + +The docker setup expects you to run a Traefik reverse proxy. It will then register itself automatically. +If your setup is different, you will need to adjust the `docker-compose.yaml` file accordingly. + +The finances containers will automatically register themselves to a running watchtower container if it is present. + +Finally run `docker-compose up -d` on your server to start the application. diff --git a/docker/finances-prod/.env.example b/docker/finances-prod/.env.example new file mode 100644 index 0000000..d32102f --- /dev/null +++ b/docker/finances-prod/.env.example @@ -0,0 +1,11 @@ + +# database configuration +DB_USER="db_user" +DB_PASSWORD="db_password" + +# prisma database url +DATABASE_URL="postgresql://$DB_USER:$DB_PASSWORD@postgres:5432/finances?schema=public" + +APPLICATION_DOMAIN="finances.thielker.xyz" +COOKIE_DOMAIN="$APPLICATION_DOMAIN" +ORIGIN="https://$APPLICATION_DOMAIN" diff --git a/docker/finances-prod/docker-compose.yaml b/docker/finances-prod/docker-compose.yaml index 3471f5c..7017d47 100644 --- a/docker/finances-prod/docker-compose.yaml +++ b/docker/finances-prod/docker-compose.yaml @@ -25,7 +25,7 @@ services: restart: unless-stopped labels: - "traefik.enable=true" - - "traefik.http.routers.xyz-next-finances.rule=Host(`finances.thielker.xyz`)" + - "traefik.http.routers.xyz-next-finances.rule=Host(`${APPLICATION_DOMAIN}`)" - "traefik.http.routers.xyz-next-finances.entrypoints=web, websecure" - "traefik.http.routers.xyz-next-finances.tls=true" - "traefik.http.routers.xyz-next-finances.tls.certresolver=lets-encrypt" @@ -46,7 +46,7 @@ services: restart: unless-stopped labels: - "traefik.enable=true" - - "traefik.http.routers.xyz-next-finances-studio.rule=Host(`studio.finances.thielker.xyz`)" + - "traefik.http.routers.xyz-next-finances-studio.rule=Host(`studio.${APPLICATION_DOMAIN}`)" - "traefik.http.routers.xyz-next-finances-studio.entrypoints=web, websecure" - "traefik.http.services.xyz-next-finances-studio.loadbalancer.server.port=5555" - "traefik.http.routers.xyz-next-finances-studio.tls=true" diff --git a/docker/finances-prod/traefik.toml b/docker/finances-prod/traefik.toml new file mode 100644 index 0000000..3738e71 --- /dev/null +++ b/docker/finances-prod/traefik.toml @@ -0,0 +1,13 @@ +[entryPoints] +[entryPoints.web] +address = ":80" +# [entryPoints.web.http.redirections.entryPoint] +# to = "websecure" +# scheme = "https" + +[entryPoints.websecure] +address = ":443" + +[providers.docker] +watch = true +network = "web" diff --git a/docker/finances-prod/traefik_setup.sh b/docker/finances-prod/traefik_setup.sh new file mode 100644 index 0000000..18a913a --- /dev/null +++ b/docker/finances-prod/traefik_setup.sh @@ -0,0 +1,13 @@ + +# +# run this container on your server to use traefik as a reverse proxy +# +docker run -d \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $PWD/traefik.toml:/traefik.toml \ + -p 80:80 \ + -p 443:443 \ + --restart unless-stopped \ + --network web \ + --name traefik \ + traefik:v2.10 diff --git a/docker/finances-prod/watchtower_setup.sh b/docker/finances-prod/watchtower_setup.sh new file mode 100644 index 0000000..b308dd1 --- /dev/null +++ b/docker/finances-prod/watchtower_setup.sh @@ -0,0 +1,13 @@ + +# +# run this container on your server to keep the labeled containers up to date +# +# run 'docker login' to authenticate with your docker hub account +# label your containers with 'com.centurylinklabs.watchtower.enable=true' to enable watchtower +# +docker run -d \ + --name watchtower \ + --restart unless-stopped \ + -v $HOME/.docker/config.json:/config.json \ + -v /var/run/docker.sock:/var/run/docker.sock \ + containrrr/watchtower -s "*/30 * * * * *" --label-enable diff --git a/next.config.mjs b/next.config.mjs index 99d9088..7db8086 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,5 +1,11 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { +import withSerwistInit from '@serwist/next'; + +const withSerwist = withSerwistInit({ + swSrc: 'src/app/service-worker.ts', + swDest: 'public/sw.js', +}); + +export default withSerwist({ webpack: (config) => { config.externals.push( '@node-rs/argon2', @@ -11,6 +17,4 @@ const nextConfig = { env: { appVersion: process.env.npm_package_version, }, -}; - -export default nextConfig; +}); diff --git a/package-lock.json b/package-lock.json index 744cf02..6f29471 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,12 @@ { "name": "next-finances", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-finances", - "version": "1.1.0", - "license": "MIT", + "version": "1.2.0", "dependencies": { "@hookform/resolvers": "^3.3.4", "@lucia-auth/adapter-prisma": "^4.0.0", @@ -22,6 +21,9 @@ "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", + "@serwist/next": "^8.4.4", + "@serwist/precaching": "^8.4.4", + "@serwist/sw": "^8.4.4", "@tanstack/react-table": "^8.13.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -311,6 +313,16 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", @@ -1502,12 +1514,382 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz", + "integrity": "sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz", + "integrity": "sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz", + "integrity": "sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz", + "integrity": "sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz", + "integrity": "sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz", + "integrity": "sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz", + "integrity": "sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz", + "integrity": "sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz", + "integrity": "sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz", + "integrity": "sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz", + "integrity": "sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz", + "integrity": "sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz", + "integrity": "sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rushstack/eslint-patch": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz", "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", "dev": true }, + "node_modules/@serwist/background-sync": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/background-sync/-/background-sync-8.4.4.tgz", + "integrity": "sha512-Rt1BwpUZb1G9lj4znrshC6di5hwB5xEmhilzFbHEbZuiFfeCNwn8tMc8JUMoGdj2Btx4/XlUDgvsFvqRi1BkTA==", + "dependencies": { + "@serwist/core": "8.4.4", + "idb": "8.0.0" + } + }, + "node_modules/@serwist/broadcast-update": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/broadcast-update/-/broadcast-update-8.4.4.tgz", + "integrity": "sha512-hN/S9xbVpg17NLcHqa/vEIMmZrpH/MnFN33bT1Wsh11QAj/IY6aOu5nq/8vbppMPM9OlkKimYT6bJDNRSbsIDA==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/build": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/build/-/build-8.4.4.tgz", + "integrity": "sha512-24BfRoVfbzlWAlra9m8fU6vutCZjcZ5QHaL2LEAexPBixsDGaTVjBrfbNipEbM7OejLPp99MqZaj3U3EgWU2ag==", + "dependencies": { + "@apideck/better-ajv-errors": "0.3.6", + "@serwist/background-sync": "8.4.4", + "@serwist/broadcast-update": "8.4.4", + "@serwist/cacheable-response": "8.4.4", + "@serwist/core": "8.4.4", + "@serwist/expiration": "8.4.4", + "@serwist/google-analytics": "8.4.4", + "@serwist/precaching": "8.4.4", + "@serwist/routing": "8.4.4", + "ajv": "8.12.0", + "common-tags": "1.8.2", + "fast-json-stable-stringify": "2.1.0", + "fs-extra": "11.2.0", + "glob": "10.3.10", + "rollup": "4.9.1", + "source-map": "0.8.0-beta.0", + "upath": "2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@serwist/build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/@serwist/build/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@serwist/build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/@serwist/cacheable-response": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/cacheable-response/-/cacheable-response-8.4.4.tgz", + "integrity": "sha512-oSPPaGDmfDhtAQfHC5LIIyYF2djCprYHdWntD8IQygnjEjbh2MP6xnD7zPD+Ztghlg9VX0aGRxEAFT8rDhW7tQ==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/core": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/core/-/core-8.4.4.tgz", + "integrity": "sha512-mB3XFoUFbfgxkqWUhn9khqZP9c+K3qsuY1WQazl7S00hd+JUH2We/FmNMoOpei5ydVK61rIX690ScfXO0FbjXw==" + }, + "node_modules/@serwist/expiration": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/expiration/-/expiration-8.4.4.tgz", + "integrity": "sha512-zn5Q3qkE2827RUDwta198QTeIWzQx+EtWikQog9LZZvXSDOuOogzYeyiuskrfvrGFLk11myOJLtpVbXpeNCHeA==", + "dependencies": { + "@serwist/core": "8.4.4", + "idb": "8.0.0" + } + }, + "node_modules/@serwist/google-analytics": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/google-analytics/-/google-analytics-8.4.4.tgz", + "integrity": "sha512-nkNLyLmSfiYeJGFtHDzRdLDcUabyTGvrjC2VAlduC97rLhfE1c1KUhWBwBO+ZR0TONsh9roJso2vooEZc2dWAQ==", + "dependencies": { + "@serwist/background-sync": "8.4.4", + "@serwist/core": "8.4.4", + "@serwist/routing": "8.4.4", + "@serwist/strategies": "8.4.4" + } + }, + "node_modules/@serwist/navigation-preload": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/navigation-preload/-/navigation-preload-8.4.4.tgz", + "integrity": "sha512-cMna+4avA66mHuZeVBxRYViy7/O+fR+NLSLkSl5TRdx6jdDFBnsBVj6LOWcohuGmtJ/XUV0ycpxyGSUxJCjVag==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/next": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/next/-/next-8.4.4.tgz", + "integrity": "sha512-WUTMTqjGl4uEO61olHkbqTWCV8bsNSYpzHNmhWksg+RXJJKCENU3Io+UcJlPX+oBJVzlIHP0cHtPsQZx7I+2sw==", + "dependencies": { + "@serwist/build": "8.4.4", + "@serwist/webpack-plugin": "8.4.4", + "@serwist/window": "8.4.4", + "clean-webpack-plugin": "4.0.0", + "fast-glob": "3.3.2" + }, + "peerDependencies": { + "next": ">=14.0.0", + "webpack": ">=5.9.0" + } + }, + "node_modules/@serwist/precaching": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/precaching/-/precaching-8.4.4.tgz", + "integrity": "sha512-2HUiDKRpHxNirH37UnIuLx6Zc6HaIAgpTE2qWT6BfzqX1Fbmn2Zk67tvBTfCITWemCw1/dUH8VzI1k6vC3XmtA==", + "dependencies": { + "@serwist/core": "8.4.4", + "@serwist/routing": "8.4.4", + "@serwist/strategies": "8.4.4" + } + }, + "node_modules/@serwist/range-requests": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/range-requests/-/range-requests-8.4.4.tgz", + "integrity": "sha512-1VVhL42XnjFJKszAhgwkCUjcK4j28eudWhfGaYey5NYBk0xNqXNGTOuPBjvr5mLMo/NkmapiwDI/htAqOuK7Wg==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/routing": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/routing/-/routing-8.4.4.tgz", + "integrity": "sha512-TimZ54n5UpQoJMjGdLIXQ1VJlQ669yfJ8scdfZKwRjggILJ3f1rI13N8vicetwZMGqveTz2Q9sVYIXaeQuFmQg==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/strategies": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/strategies/-/strategies-8.4.4.tgz", + "integrity": "sha512-Yr/wKD16vXUqVbyfHhLM25qRWTtWgNJnY8Bot0yjCV/BvHM81cOIkG3sE76FIjcUntCvfemhaN9Zx53fbRzLPQ==", + "dependencies": { + "@serwist/core": "8.4.4" + } + }, + "node_modules/@serwist/sw": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/sw/-/sw-8.4.4.tgz", + "integrity": "sha512-TyFlhoJpo/S1hj7+j1iiELwoU4idKvqvITBYFzpYEUK3h/qXJ+eiNiGT1hl2R0znz8p35Hp3hxbyGYFt4BYpKw==", + "dependencies": { + "@serwist/background-sync": "8.4.4", + "@serwist/broadcast-update": "8.4.4", + "@serwist/cacheable-response": "8.4.4", + "@serwist/core": "8.4.4", + "@serwist/expiration": "8.4.4", + "@serwist/google-analytics": "8.4.4", + "@serwist/navigation-preload": "8.4.4", + "@serwist/precaching": "8.4.4", + "@serwist/range-requests": "8.4.4", + "@serwist/routing": "8.4.4", + "@serwist/strategies": "8.4.4" + } + }, + "node_modules/@serwist/webpack-plugin": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/webpack-plugin/-/webpack-plugin-8.4.4.tgz", + "integrity": "sha512-A4ONzZzVJNgY3vddhrV7lwRuLYHQQw/SxW8iEZ62VQR3Xvx/sdrovKBJ+cXokt7kIcNgNRtPAxAeddptoirZDw==", + "dependencies": { + "@serwist/build": "8.4.4", + "fast-json-stable-stringify": "2.1.0", + "upath": "2.0.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "webpack": "4.4.0 || ^5.9.0" + } + }, + "node_modules/@serwist/window": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@serwist/window/-/window-8.4.4.tgz", + "integrity": "sha512-UB/nHixanuf/7SBY4P0w2/H0NU1igM+gAg26bTFcHHkE3KsvwK7xWZMIAWeifzZK7w6xGJpK5INbjGuB9NySvg==", + "dependencies": { + "@serwist/core": "8.4.4", + "@types/trusted-types": "2.0.7" + } + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -1571,17 +1953,62 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, + "node_modules/@types/eslint": { + "version": "8.56.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", + "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "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==", + "peer": true + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "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==", + "peer": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "node_modules/@types/node": { "version": "20.11.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz", "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==", - "devOptional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1618,6 +2045,11 @@ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", "devOptional": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", @@ -1751,11 +2183,168 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "peer": true + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -1763,6 +2352,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1785,7 +2383,6 @@ "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", @@ -1797,6 +2394,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1911,6 +2517,14 @@ "node": ">=8" } }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array.prototype.filter": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", @@ -2153,7 +2767,6 @@ "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" @@ -2174,7 +2787,6 @@ "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2202,6 +2814,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "peer": true + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2318,6 +2936,15 @@ "node": ">= 6" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/class-variance-authority": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", @@ -2337,6 +2964,20 @@ "node": ">=6" } }, + "node_modules/clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dependencies": { + "del": "^4.1.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.0 <6.0.0" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -2387,11 +3028,18 @@ "node": ">= 6" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, "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 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/create-require": { "version": "1.1.1", @@ -2501,6 +3149,95 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "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/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2566,8 +3303,7 @@ "node_modules/electron-to-chromium": { "version": "1.4.695", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.695.tgz", - "integrity": "sha512-eMijZmeqPtm774pCZIOrfUHMs/7ls++W1sLhxwqgu8KQ8E2WmMtzwyqOMt0XXUJ3HTIPfuwlfwF+I5cwnfItBA==", - "dev": true + "integrity": "sha512-eMijZmeqPtm774pCZIOrfUHMs/7ls++W1sLhxwqgu8KQ8E2WmMtzwyqOMt0XXUJ3HTIPfuwlfwF+I5cwnfItBA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -2578,7 +3314,6 @@ "version": "5.15.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz", "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -2695,6 +3430,12 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "peer": true + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -2739,7 +3480,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, "engines": { "node": ">=6" } @@ -3142,7 +3882,6 @@ "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" }, @@ -3154,7 +3893,6 @@ "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" } @@ -3168,11 +3906,19 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "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 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -3203,8 +3949,7 @@ "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 + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -3316,11 +4061,23 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "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 + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3458,6 +4215,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "peer": true + }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3566,7 +4329,6 @@ "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" } @@ -3633,6 +4395,11 @@ "node": ">= 0.4" } }, + "node_modules/idb": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", + "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -3671,7 +4438,6 @@ "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" @@ -3680,8 +4446,7 @@ "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 + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.7", @@ -3911,6 +4676,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -4080,6 +4875,35 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", @@ -4111,11 +4935,21 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "peer": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "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 + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -4135,6 +4969,25 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -4177,6 +5030,14 @@ "node": ">=0.10" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4203,6 +5064,15 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4224,6 +5094,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4356,6 +5231,12 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "devOptional": true }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4376,11 +5257,31 @@ "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": "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" }, @@ -4444,6 +5345,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "peer": true + }, "node_modules/next": { "version": "14.1.3", "resolved": "https://registry.npmjs.org/next/-/next-14.1.3.tgz", @@ -4529,8 +5436,7 @@ "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 + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4679,7 +5585,6 @@ "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" } @@ -4740,6 +5645,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4765,11 +5678,15 @@ "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-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4831,6 +5748,25 @@ "node": ">=0.10.0" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -5029,7 +5965,6 @@ "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" } @@ -5053,6 +5988,15 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -5240,6 +6184,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -5318,6 +6270,34 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.1.tgz", + "integrity": "sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.1", + "@rollup/rollup-android-arm64": "4.9.1", + "@rollup/rollup-darwin-arm64": "4.9.1", + "@rollup/rollup-darwin-x64": "4.9.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.1", + "@rollup/rollup-linux-arm64-gnu": "4.9.1", + "@rollup/rollup-linux-arm64-musl": "4.9.1", + "@rollup/rollup-linux-riscv64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-musl": "4.9.1", + "@rollup/rollup-win32-arm64-msvc": "4.9.1", + "@rollup/rollup-win32-ia32-msvc": "4.9.1", + "@rollup/rollup-win32-x64-msvc": "4.9.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", @@ -5358,6 +6338,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -5383,6 +6383,24 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -5410,6 +6428,15 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -5508,6 +6535,17 @@ "react-dom": "^18.0.0" } }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5516,6 +6554,25 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -5831,11 +6888,68 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/terser": { + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5872,6 +6986,14 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/ts-api-utils": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", @@ -6083,14 +7205,29 @@ "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==", - "devOptional": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "engines": { + "node": ">=4", + "yarn": "*" + } }, "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", @@ -6120,7 +7257,6 @@ "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" } @@ -6197,6 +7333,112 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/webpack": { + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6377,8 +7619,7 @@ "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 + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "4.0.0", diff --git a/package.json b/package.json index 85074f6..a503c4d 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "name": "next-finances", "description": "A finances application to keep track of my personal spendings", "homepage": "https://github.com/MarkusThielker/next-finances", - "version": "1.1.0", - "license": "MIT", + "version": "1.2.0", "author": { "name": "Markus Thielker" }, @@ -32,6 +31,9 @@ "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", + "@serwist/next": "^8.4.4", + "@serwist/precaching": "^8.4.4", + "@serwist/sw": "^8.4.4", "@tanstack/react-table": "^8.13.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", diff --git a/prisma/migrations/20240317102723_add_default_category_to_entity/migration.sql b/prisma/migrations/20240317102723_add_default_category_to_entity/migration.sql new file mode 100644 index 0000000..f7767eb --- /dev/null +++ b/prisma/migrations/20240317102723_add_default_category_to_entity/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +ALTER TABLE "entities" + ADD COLUMN "default_category_id" INTEGER; + +-- AddForeignKey +ALTER TABLE "entities" + ADD CONSTRAINT "entities_default_category_id_fkey" FOREIGN KEY ("default_category_id") REFERENCES "categories" ("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5d51152..1e9f6e1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -34,13 +34,15 @@ model Session { } model Entity { - id Int @id @default(autoincrement()) - userId String @map("user_id") - user User @relation(fields: [userId], references: [id]) - name String - type EntityType - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") + id Int @id @default(autoincrement()) + userId String @map("user_id") + user User @relation(fields: [userId], references: [id]) + name String + type EntityType + defaultCategory Category? @relation(fields: [defaultCategoryId], references: [id]) + defaultCategoryId Int? @map("default_category_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") paymentsAsPayor Payment[] @relation("PayorEntity") paymentsAsPayee Payment[] @relation("PayeeEntity") @@ -84,6 +86,7 @@ model Category { updatedAt DateTime @updatedAt @map("updated_at") payments Payment[] + Entity Entity[] @@unique(fields: [userId, name]) @@map("categories") diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..c4d2999 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "Finances", + "short_name": "Finances", + "icons": [ + { + "src": "/logo_white.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#0B0908", + "background_color": "#0B0908", + "start_url": "/", + "display": "standalone", + "orientation": "portrait" +} diff --git a/src/app/account/page.tsx b/src/app/account/page.tsx index 4bc3d46..0023029 100644 --- a/src/app/account/page.tsx +++ b/src/app/account/page.tsx @@ -5,11 +5,11 @@ import { redirect } from 'next/navigation'; import signOut from '@/lib/actions/signOut'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; -import SignOutForm from '@/components/form/signOutForm'; import { URL_SIGN_IN } from '@/lib/constants'; -import GenerateSampleDataForm from '@/components/form/generateSampleDataForm'; import generateSampleData from '@/lib/actions/generateSampleData'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; +import { ServerActionTrigger } from '@/components/form/serverActionTrigger'; +import accountDelete from '@/lib/actions/accountDelete'; export default async function AccountPage() { @@ -20,21 +20,21 @@ export default async function AccountPage() { } let paymentCount = 0; - paymentCount = await prismaClient.payment.count({ + paymentCount = await prisma.payment.count({ where: { userId: user.id, }, }); let entityCount = 0; - entityCount = await prismaClient.entity.count({ + entityCount = await prisma.entity.count({ where: { userId: user.id, }, }); let categoryCount = 0; - categoryCount = await prismaClient.category.count({ + categoryCount = await prisma.category.count({ where: { userId: user.id, }, @@ -81,13 +81,31 @@ export default async function AccountPage() { - + + + Delete Account + + + Sign Out + { process.env.NODE_ENV === 'development' && ( - + + Generate sample data + ) } -
diff --git a/src/app/categories/page.tsx b/src/app/categories/page.tsx index f7bee73..73c6251 100644 --- a/src/app/categories/page.tsx +++ b/src/app/categories/page.tsx @@ -1,5 +1,5 @@ import { getUser } from '@/auth'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import React from 'react'; import CategoryPageClientContent from '@/components/categoryPageClientComponents'; import categoryCreateUpdate from '@/lib/actions/categoryCreateUpdate'; @@ -9,7 +9,7 @@ export default async function CategoriesPage() { const user = await getUser(); - const categories = await prismaClient.category.findMany({ + const categories = await prisma.category.findMany({ where: { userId: user?.id, }, diff --git a/src/app/entities/columns.tsx b/src/app/entities/columns.tsx index 90b3754..c68da1b 100644 --- a/src/app/entities/columns.tsx +++ b/src/app/entities/columns.tsx @@ -1,12 +1,13 @@ 'use client'; import { ColumnDef } from '@tanstack/react-table'; -import { Entity } from '@prisma/client'; +import { Category, Entity } from '@prisma/client'; import { CellContext, ColumnDefTemplate } from '@tanstack/table-core'; import { format } from 'date-fns'; export const columns = ( actionCell: ColumnDefTemplate>, + categories: Category[], ) => { return [ @@ -19,6 +20,30 @@ export const columns = ( header: 'Type', size: 100, }, + { + accessorKey: 'defaultCategoryId', + header: 'Default Category', + cell: ({row}) => { + const category = categories.find((category) => category.id === row.original.defaultCategoryId); + return ( + <> + { + category && ( +
+ + + +

{category?.name ?? '-'}

+
+ ) + } + + + ); + }, + size: 200, + }, { accessorKey: 'createdAt', header: 'Created at', diff --git a/src/app/entities/page.tsx b/src/app/entities/page.tsx index 6f41ba5..e39176c 100644 --- a/src/app/entities/page.tsx +++ b/src/app/entities/page.tsx @@ -1,4 +1,4 @@ -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import React from 'react'; import EntityPageClientContent from '@/components/entityPageClientComponents'; @@ -9,7 +9,7 @@ export default async function EntitiesPage() { const user = await getUser(); - const entities = await prismaClient.entity.findMany({ + const entities = await prisma.entity.findMany({ where: { userId: user?.id, }, @@ -23,9 +23,24 @@ export default async function EntitiesPage() { ], }); + const categories = await prisma.category.findMany({ + where: { + userId: user?.id, + }, + orderBy: [ + { + name: 'asc', + }, + { + id: 'asc', + }, + ], + }); + return ( diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 95bd21f..fc2e150 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from 'next'; +import type { Viewport } from 'next'; import { Inter } from 'next/font/google'; import './globals.css'; import { cn } from '@/lib/utils'; @@ -8,9 +8,31 @@ import Navigation from '@/components/navigation'; const inter = Inter({subsets: ['latin']}); -export const metadata: Metadata = { - title: 'Finances', - description: 'Track your finances with ease', +const APP_NAME = 'Finances'; +const APP_DEFAULT_TITLE = 'Finances'; +const APP_TITLE_TEMPLATE = `%s | ${APP_DEFAULT_TITLE}`; +const APP_DESCRIPTION = 'Track your finances with ease'; + +export const metadata = { + applicationName: APP_NAME, + title: { + default: APP_DEFAULT_TITLE, + template: APP_TITLE_TEMPLATE, + }, + description: APP_DESCRIPTION, + appleWebApp: { + capable: true, + statusBarStyle: 'default', + title: APP_DEFAULT_TITLE, + }, + formatDetection: { + telephone: false, + }, +}; + +export const viewport: Viewport = { + themeColor: '#0B0908', + width: 'device-width', }; export default function RootLayout({ @@ -20,6 +42,13 @@ export default function RootLayout({ }>) { return ( + + + +
diff --git a/src/app/page.tsx b/src/app/page.tsx index 54450d0..72c34da 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Category, Entity, EntityType } from '@prisma/client'; import { Scope, ScopeType } from '@/lib/types/scope'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import DashboardPageClient from '@/components/dashboardPageClientComponents'; @@ -25,7 +25,7 @@ export default async function DashboardPage(props: { searchParams?: { scope: Sco const scope = Scope.of(props.searchParams?.scope || ScopeType.ThisMonth); // get all payments in the current scope - const payments = await prismaClient.payment.findMany({ + const payments = await prisma.payment.findMany({ where: { userId: user?.id, date: { @@ -108,6 +108,7 @@ export default async function DashboardPage(props: { searchParams?: { scope: Sco userId: '', name: 'Other', type: EntityType.Entity, + defaultCategoryId: null, createdAt: new Date(), updatedAt: new Date(), }, diff --git a/src/app/payments/columns.tsx b/src/app/payments/columns.tsx index d3b950e..e462267 100644 --- a/src/app/payments/columns.tsx +++ b/src/app/payments/columns.tsx @@ -55,13 +55,19 @@ export const columns = ( cell: ({row}) => { const category = categories.find((category) => category.id === row.original.categoryId); return ( -
- - - -

{category?.name ?? '-'}

-
+ <> + { + category && ( +
+ + + +

{category?.name ?? '-'}

+
+ ) + } + ); }, size: 200, diff --git a/src/app/payments/page.tsx b/src/app/payments/page.tsx index bbec906..f1ffa66 100644 --- a/src/app/payments/page.tsx +++ b/src/app/payments/page.tsx @@ -1,5 +1,5 @@ import { getUser } from '@/auth'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import React from 'react'; import PaymentPageClientContent from '@/components/paymentPageClientComponents'; import paymentCreateUpdate from '@/lib/actions/paymentCreateUpdate'; @@ -9,7 +9,7 @@ export default async function PaymentsPage() { const user = await getUser(); - const payments = await prismaClient.payment.findMany({ + const payments = await prisma.payment.findMany({ where: { userId: user?.id, }, @@ -23,7 +23,7 @@ export default async function PaymentsPage() { ], }); - const entities = await prismaClient.entity.findMany({ + const entities = await prisma.entity.findMany({ where: { userId: user?.id, }, @@ -37,7 +37,7 @@ export default async function PaymentsPage() { ], }); - const categories = await prismaClient.category.findMany({ + const categories = await prisma.category.findMany({ where: { userId: user?.id, }, diff --git a/src/app/service-worker.ts b/src/app/service-worker.ts new file mode 100644 index 0000000..f68e452 --- /dev/null +++ b/src/app/service-worker.ts @@ -0,0 +1,15 @@ +import { defaultCache } from '@serwist/next/browser'; +import type { PrecacheEntry } from '@serwist/precaching'; +import { installSerwist } from '@serwist/sw'; + +declare const self: ServiceWorkerGlobalScope & { + __SW_MANIFEST: (PrecacheEntry | string)[] | undefined; +}; + +installSerwist({ + precacheEntries: self.__SW_MANIFEST, + skipWaiting: true, + clientsClaim: true, + navigationPreload: true, + runtimeCaching: defaultCache, +}); diff --git a/src/auth.ts b/src/auth.ts index 26cdb6c..ae752a6 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,9 +1,9 @@ import { Lucia } from 'lucia'; import { PrismaAdapter } from '@lucia-auth/adapter-prisma'; import { cookies } from 'next/headers'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; -const adapter = new PrismaAdapter(prismaClient.session, prismaClient.user); +const adapter = new PrismaAdapter(prisma.session, prisma.user); export const lucia = new Lucia(adapter, { sessionCookie: { diff --git a/src/components/entityPageClientComponents.tsx b/src/components/entityPageClientComponents.tsx index 3845ee1..f3f3085 100644 --- a/src/components/entityPageClientComponents.tsx +++ b/src/components/entityPageClientComponents.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Entity } from '@prisma/client'; +import { Category, Entity } from '@prisma/client'; import React, { useState } from 'react'; import { CellContext } from '@tanstack/table-core'; import { Button } from '@/components/ui/button'; @@ -27,8 +27,9 @@ import { import { useMediaQuery } from '@/lib/hooks/useMediaQuery'; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer'; -export default function EntityPageClientContent({entities, onSubmit, onDelete, className}: { +export default function EntityPageClientContent({entities, categories, onSubmit, onDelete, className}: { entities: Entity[], + categories: Category[], onSubmit: (data: z.infer) => Promise, onDelete: (id: number) => Promise, className: string, @@ -146,6 +147,7 @@ export default function EntityPageClientContent({entities, onSubmit, onDelete, c @@ -167,6 +169,7 @@ export default function EntityPageClientContent({entities, onSubmit, onDelete, c @@ -184,7 +187,7 @@ export default function EntityPageClientContent({entities, onSubmit, onDelete, c {/* Data Table */} diff --git a/src/components/form/entityForm.tsx b/src/components/form/entityForm.tsx index 274c290..953aca8 100644 --- a/src/components/form/entityForm.tsx +++ b/src/components/form/entityForm.tsx @@ -12,11 +12,13 @@ import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; import { sonnerContent } from '@/components/ui/sonner'; import { entityFormSchema } from '@/lib/form-schemas/entityFormSchema'; -import { Entity, EntityType } from '@prisma/client'; +import { Category, Entity, EntityType } from '@prisma/client'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { AutoCompleteInput } from '@/components/ui/auto-complete-input'; -export default function EntityForm({value, onSubmit, className}: { +export default function EntityForm({value, categories, onSubmit, className}: { value: Entity | undefined, + categories: Category[], onSubmit: (data: z.infer) => Promise className?: string }) { @@ -29,6 +31,7 @@ export default function EntityForm({value, onSubmit, className}: { id: value?.id ?? undefined, name: value?.name ?? '', type: value?.type ?? EntityType.Entity, + defaultCategoryId: value?.defaultCategoryId ?? undefined, }, }); @@ -40,6 +43,13 @@ export default function EntityForm({value, onSubmit, className}: { } }; + const categoriesMapped = categories?.map((category) => { + return { + label: category.name, + value: category.id, + }; + }) ?? []; + return (
@@ -94,6 +104,22 @@ export default function EntityForm({value, onSubmit, className}: { )} /> + ( + + Category + + + + + + )} + />
diff --git a/src/components/form/generateSampleDataForm.tsx b/src/components/form/generateSampleDataForm.tsx deleted file mode 100644 index c8677be..0000000 --- a/src/components/form/generateSampleDataForm.tsx +++ /dev/null @@ -1,23 +0,0 @@ -'use client'; - -import { Button } from '@/components/ui/button'; -import React from 'react'; -import { useRouter } from 'next/navigation'; -import { toast } from 'sonner'; -import { sonnerContent } from '@/components/ui/sonner'; -import { ActionResponse } from '@/lib/types/actionResponse'; - -export default function GenerateSampleDataForm({onSubmit}: { onSubmit: () => Promise }) { - - const router = useRouter(); - - const handleSubmit = async () => { - const response = await onSubmit(); - toast(sonnerContent(response)); - router.refresh(); - }; - - return ( - - ); -} diff --git a/src/components/form/paymentForm.tsx b/src/components/form/paymentForm.tsx index 705587e..5e9c44d 100644 --- a/src/components/form/paymentForm.tsx +++ b/src/components/form/paymentForm.tsx @@ -166,7 +166,17 @@ export default function PaymentForm({value, entities, categories, onSubmit, clas placeholder="Select payee" items={entitiesMapped} next={categoryRef} - {...field} /> + {...field} + onChange={(e) => { + field.onChange(e); + if (e && e.target.value) { + const entity = entities.find((entity) => entity.id === Number(e.target.value)); + console.log(entity?.defaultCategoryId); + if (entity?.defaultCategoryId !== null) { + form.setValue('categoryId', entity?.defaultCategoryId); + } + } + }}/> diff --git a/src/components/form/serverActionTrigger.tsx b/src/components/form/serverActionTrigger.tsx new file mode 100644 index 0000000..5fe03da --- /dev/null +++ b/src/components/form/serverActionTrigger.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { buttonVariants } from '@/components/ui/button'; +import React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cn } from '@/lib/utils'; +import { useRouter } from 'next/navigation'; +import { toast } from 'sonner'; +import { sonnerContent } from '@/components/ui/sonner'; +import type { VariantProps } from 'class-variance-authority'; +import { ActionResponse } from '@/lib/types/actionResponse'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; + +export interface ConfirmationDialogProps { + title: string; + description?: string; + actionText?: string; +} + +export interface ButtonWithActionProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; + dialog?: ConfirmationDialogProps; + action: () => Promise>; + callback?: (data: T) => void; +} + +const ServerActionTrigger = React.forwardRef( + ({className, variant, size, asChild = false, ...props}, ref) => { + + const router = useRouter(); + + const Comp = asChild ? Slot : 'button'; + + const handleSubmit = async () => { + const response = await props.action(); + toast(sonnerContent(response)); + if (props.callback) { + props.callback(response); + } + if (response.redirect) { + router.push(response.redirect); + } + }; + + return props.dialog ? ( + + + + + + + {props.dialog.title} + {props.dialog?.description && ( + + {props.dialog.description} + + )} + + + + Cancel + + + {props.dialog.actionText || 'Confirm'} + + + + + ) : ( + + ); + }, +); +ServerActionTrigger.displayName = 'ServerActionTrigger'; + +export { ServerActionTrigger }; diff --git a/src/components/form/signOutForm.tsx b/src/components/form/signOutForm.tsx deleted file mode 100644 index 9dd51fe..0000000 --- a/src/components/form/signOutForm.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'use client'; - -import { ActionResponse } from '@/lib/types/actionResponse'; -import { Button } from '@/components/ui/button'; -import React from 'react'; -import { useRouter } from 'next/navigation'; -import { toast } from 'sonner'; -import { sonnerContent } from '@/components/ui/sonner'; - -export default function SignOutForm({onSubmit}: { onSubmit: () => Promise }) { - - const router = useRouter(); - - const handleSignOut = async () => { - const response = await onSubmit(); - toast(sonnerContent(response)); - if (response.redirect) { - router.push(response.redirect); - } - }; - - return ( - - ); -} diff --git a/src/components/ui/auto-complete-input.tsx b/src/components/ui/auto-complete-input.tsx index 29363e6..e6e3d46 100644 --- a/src/components/ui/auto-complete-input.tsx +++ b/src/components/ui/auto-complete-input.tsx @@ -3,6 +3,8 @@ import * as React from 'react'; import { useEffect, useState } from 'react'; import { cn } from '@/lib/utils'; +import { X } from 'lucide-react'; +import { Button } from '@/components/ui/button'; export interface AutoCompleteInputProps extends React.InputHTMLAttributes { @@ -13,12 +15,12 @@ export interface AutoCompleteInputProps const AutoCompleteInput = React.forwardRef( ({className, type, ...props}, ref) => { - const [value, setValue] = useState(getInitialValue()); + const [value, setValue] = useState(getNameOfPropValue()); const [open, setOpen] = useState(false); const [lastKey, setLastKey] = useState(''); const [filteredItems, setFilteredItems] = useState(props.items); - function getInitialValue() { + function getNameOfPropValue() { if (!props.items) { return ''; @@ -50,6 +52,15 @@ const AutoCompleteInput = React.forwardRef { + console.log('Prop value changed', value, props.value); + if (props.value) { + setValue(getNameOfPropValue()); + } else { + setValue(''); + } + }, [props.value]); + return (
+ { + value.length > 0 && ( + + ) + } { open && (
{ + 'use server'; + + const user = await getUser(); + + if (!user) { + return { + type: 'error', + message: 'You aren\'t signed in.', + redirect: URL_SIGN_IN, + }; + } + + await prisma.payment.deleteMany({ + where: { + userId: user.id, + }, + }); + + await prisma.entity.deleteMany({ + where: { + userId: user.id, + }, + }); + + await prisma.category.deleteMany({ + where: { + userId: user.id, + }, + }); + + await prisma.session.deleteMany({ + where: { + userId: user.id, + }, + }); + + await prisma.user.delete({ + where: { + id: user.id, + }, + }); + + const sessionCookie = lucia.createBlankSessionCookie(); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + + return { + type: 'success', + message: 'Your account was removed.', + redirect: URL_SIGN_IN, + }; +} diff --git a/src/lib/actions/categoryCreateUpdate.ts b/src/lib/actions/categoryCreateUpdate.ts index a7796d5..11bdd5f 100644 --- a/src/lib/actions/categoryCreateUpdate.ts +++ b/src/lib/actions/categoryCreateUpdate.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import { ActionResponse } from '@/lib/types/actionResponse'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import { URL_SIGN_IN } from '@/lib/constants'; import { categoryFormSchema } from '@/lib/form-schemas/categoryFormSchema'; @@ -25,7 +25,7 @@ export default async function categoryCreateUpdate({ // create/update category try { if (id) { - await prismaClient.category.update({ + await prisma.category.update({ where: { id: id, }, @@ -42,7 +42,7 @@ export default async function categoryCreateUpdate({ message: `'${name}' updated`, }; } else { - await prismaClient.category.create({ + await prisma.category.create({ data: { userId: user.id, name: name, diff --git a/src/lib/actions/categoryDelete.ts b/src/lib/actions/categoryDelete.ts index 11cc496..8773fe3 100644 --- a/src/lib/actions/categoryDelete.ts +++ b/src/lib/actions/categoryDelete.ts @@ -1,5 +1,5 @@ import { ActionResponse } from '@/lib/types/actionResponse'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import { URL_SIGN_IN } from '@/lib/constants'; @@ -25,7 +25,7 @@ export default async function categoryDelete(id: number): Promise): Promise { 'use server'; @@ -25,13 +26,14 @@ export default async function entityCreateUpdate({ // create/update entity try { if (id) { - await prismaClient.entity.update({ + await prisma.entity.update({ where: { id: id, }, data: { name: name, type: type, + defaultCategoryId: defaultCategoryId ?? null, }, }, ); @@ -42,11 +44,12 @@ export default async function entityCreateUpdate({ message: `${type} '${name}' updated`, }; } else { - await prismaClient.entity.create({ + await prisma.entity.create({ data: { userId: user.id, name: name, type: type, + defaultCategoryId: defaultCategoryId ?? null, }, }); diff --git a/src/lib/actions/entityDelete.ts b/src/lib/actions/entityDelete.ts index cb2dd45..2e0e3eb 100644 --- a/src/lib/actions/entityDelete.ts +++ b/src/lib/actions/entityDelete.ts @@ -1,5 +1,5 @@ import { ActionResponse } from '@/lib/types/actionResponse'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import { URL_SIGN_IN } from '@/lib/constants'; @@ -25,7 +25,7 @@ export default async function entityDelete(id: number): Promise } // check that entity is associated with user - const entity = await prismaClient.entity.findFirst({ + const entity = await prisma.entity.findFirst({ where: { id: id, userId: user.id, @@ -40,7 +40,7 @@ export default async function entityDelete(id: number): Promise // delete entity try { - await prismaClient.entity.delete({ + await prisma.entity.delete({ where: { id: entity.id, userId: user.id, diff --git a/src/lib/actions/generateSampleData.ts b/src/lib/actions/generateSampleData.ts index 86db509..09ea36b 100644 --- a/src/lib/actions/generateSampleData.ts +++ b/src/lib/actions/generateSampleData.ts @@ -1,4 +1,4 @@ -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import type { Category, Entity } from '@prisma/client'; import { EntityType } from '@prisma/client'; import { getUser } from '@/auth'; @@ -19,12 +19,12 @@ export default async function generateSampleData(): Promise { } // Categories: create sample data - const categories: Category[] = await prismaClient.category.findMany({where: {userId: user.id}}); - if (await prismaClient.category.count({where: {userId: user.id}}) == 0) { + const categories: Category[] = await prisma.category.findMany({where: {userId: user.id}}); + if (await prisma.category.count({where: {userId: user.id}}) == 0) { console.log('Creating sample categories...'); - categories.push(await prismaClient.category.create({ + categories.push(await prisma.category.create({ data: { userId: user.id, name: 'Groceries', @@ -32,7 +32,7 @@ export default async function generateSampleData(): Promise { }, })); - categories.push(await prismaClient.category.create({ + categories.push(await prisma.category.create({ data: { userId: user.id, name: 'Drugstore items', @@ -40,7 +40,7 @@ export default async function generateSampleData(): Promise { }, })); - categories.push(await prismaClient.category.create({ + categories.push(await prisma.category.create({ data: { userId: user.id, name: 'Going out', @@ -48,7 +48,7 @@ export default async function generateSampleData(): Promise { }, })); - categories.push(await prismaClient.category.create({ + categories.push(await prisma.category.create({ data: { userId: user.id, name: 'Random stuff', @@ -56,7 +56,7 @@ export default async function generateSampleData(): Promise { }, })); - categories.push(await prismaClient.category.create({ + categories.push(await prisma.category.create({ data: { userId: user.id, name: 'Salary', @@ -69,12 +69,12 @@ export default async function generateSampleData(): Promise { console.log(categories); // Entities: create sample data - const entities: Entity[] = await prismaClient.entity.findMany({where: {userId: user.id}}); - if (await prismaClient.entity.count({where: {userId: user.id}}) == 0) { + const entities: Entity[] = await prisma.entity.findMany({where: {userId: user.id}}); + if (await prisma.entity.count({where: {userId: user.id}}) == 0) { console.log('Creating sample entities...'); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Main Account', @@ -82,7 +82,7 @@ export default async function generateSampleData(): Promise { }, })); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Company', @@ -90,7 +90,7 @@ export default async function generateSampleData(): Promise { }, })); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Supermarket 1', @@ -98,7 +98,7 @@ export default async function generateSampleData(): Promise { }, })); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Supermarket 2', @@ -106,7 +106,7 @@ export default async function generateSampleData(): Promise { }, })); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Supermarket 3', @@ -114,7 +114,7 @@ export default async function generateSampleData(): Promise { }, })); - entities.push(await prismaClient.entity.create({ + entities.push(await prisma.entity.create({ data: { userId: user.id, name: 'Supermarket 4', @@ -129,14 +129,14 @@ export default async function generateSampleData(): Promise { // Payments: create sample data console.log('Creating sample payments...'); - if (await prismaClient.payment.count({where: {userId: user.id}}) == 0) { + if (await prisma.payment.count({where: {userId: user.id}}) == 0) { for (let i = 0; i < 4; i++) { const date = new Date(); date.setDate(1); date.setMonth(date.getMonth() - i); - await prismaClient.payment.create({ + await prisma.payment.create({ data: { userId: user.id, amount: 200000, @@ -164,7 +164,7 @@ export default async function generateSampleData(): Promise { const date = new Date( new Date().getTime() - Math.floor(Math.random() * 10000000000)); - await prismaClient.payment.create({ + await prisma.payment.create({ data: { userId: user.id, amount: Math.floor( diff --git a/src/lib/actions/paymentCreateUpdate.ts b/src/lib/actions/paymentCreateUpdate.ts index 6f889ef..4a66d33 100644 --- a/src/lib/actions/paymentCreateUpdate.ts +++ b/src/lib/actions/paymentCreateUpdate.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import { ActionResponse } from '@/lib/types/actionResponse'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import { URL_SIGN_IN } from '@/lib/constants'; import { paymentFormSchema } from '@/lib/form-schemas/paymentFormSchema'; @@ -29,7 +29,7 @@ export default async function paymentCreateUpdate({ // create/update payment try { if (id) { - await prismaClient.payment.update({ + await prisma.payment.update({ where: { id: id, }, @@ -38,7 +38,7 @@ export default async function paymentCreateUpdate({ date: date, payorId: payorId, payeeId: payeeId, - categoryId: categoryId, + categoryId: categoryId ?? null, note: note, }, }, @@ -50,14 +50,14 @@ export default async function paymentCreateUpdate({ message: `Payment updated`, }; } else { - await prismaClient.payment.create({ + await prisma.payment.create({ data: { userId: user.id, amount: amount, date: date, payorId: payorId, payeeId: payeeId, - categoryId: categoryId, + categoryId: categoryId ?? null, note: note, }, }); diff --git a/src/lib/actions/paymentDelete.ts b/src/lib/actions/paymentDelete.ts index 3401e92..a3bfd16 100644 --- a/src/lib/actions/paymentDelete.ts +++ b/src/lib/actions/paymentDelete.ts @@ -1,5 +1,5 @@ import { ActionResponse } from '@/lib/types/actionResponse'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; import { getUser } from '@/auth'; import { URL_SIGN_IN } from '@/lib/constants'; @@ -25,7 +25,7 @@ export default async function paymentDelete(id: number): Promise } // check that payment is associated with user - const payment = await prismaClient.payment.findFirst({ + const payment = await prisma.payment.findFirst({ where: { id: id, userId: user.id, @@ -40,7 +40,7 @@ export default async function paymentDelete(id: number): Promise // delete payment try { - await prismaClient.payment.delete({ + await prisma.payment.delete({ where: { id: payment.id, userId: user.id, diff --git a/src/lib/actions/signIn.ts b/src/lib/actions/signIn.ts index 5168179..0e34e3d 100644 --- a/src/lib/actions/signIn.ts +++ b/src/lib/actions/signIn.ts @@ -5,12 +5,12 @@ import { cookies } from 'next/headers'; import { signInFormSchema } from '@/lib/form-schemas/signInFormSchema'; import { ActionResponse } from '@/lib/types/actionResponse'; import { URL_HOME } from '@/lib/constants'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; export default async function signIn({username, password}: z.infer): Promise { 'use server'; - const existingUser = await prismaClient.user.findFirst({ + const existingUser = await prisma.user.findFirst({ where: { username: username.toLowerCase(), }, diff --git a/src/lib/actions/signUp.ts b/src/lib/actions/signUp.ts index 1b40aff..d7ea888 100644 --- a/src/lib/actions/signUp.ts +++ b/src/lib/actions/signUp.ts @@ -6,7 +6,7 @@ import { cookies } from 'next/headers'; import { signUpFormSchema } from '@/lib/form-schemas/signUpFormSchema'; import { ActionResponse } from '@/lib/types/actionResponse'; import { URL_HOME } from '@/lib/constants'; -import { prismaClient } from '@/prisma'; +import prisma from '@/prisma'; export default async function signUp({username, password}: z.infer): Promise { 'use server'; @@ -14,7 +14,7 @@ export default async function signUp({username, password}: z.infer { type: 'success' | 'info' | 'warning' | 'error'; message: string; redirect?: string; + data?: T; } diff --git a/src/prisma.ts b/src/prisma.ts index 5e41d6f..be21813 100644 --- a/src/prisma.ts +++ b/src/prisma.ts @@ -1,3 +1,18 @@ import { PrismaClient } from '@prisma/client'; -export const prismaClient = new PrismaClient(); +const prismaClientSingleton = () => { + return new PrismaClient(); +}; + +declare global { + // noinspection ES6ConvertVarToLetConst + var prismaGlobal: undefined | ReturnType; +} + +const prisma = globalThis.prismaGlobal ?? prismaClientSingleton(); + +export default prisma; + +if (process.env.NODE_ENV !== 'production') { + globalThis.prismaGlobal = prisma +} diff --git a/tsconfig.json b/tsconfig.json index af0efb2..818c1b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,8 @@ "lib": [ "dom", "dom.iterable", - "esnext" + "esnext", + "webworker" ], "allowJs": true, "skipLibCheck": true, @@ -25,7 +26,10 @@ "@/*": [ "./src/*" ] - } + }, + "types": [ + "@serwist/next/typings" + ] }, "include": [ "next-env.d.ts",