From 6f8d586860e1eb6ef5ab6c8c21390c1ca134ca3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 02:26:01 +0300 Subject: [PATCH 01/74] chore(devops): build backend with actions --- .github/workflows/backend_build.yml | 59 +++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/backend_build.yml diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml new file mode 100644 index 00000000..238e0f94 --- /dev/null +++ b/.github/workflows/backend_build.yml @@ -0,0 +1,59 @@ +name: backend-worklow + +on: + push: + paths: + - 'backend/**' + - docker-compose.yml + - docker-compose.dev.yml + - '.github/workflows/backend_build.yml' + + pull_request: + branches: + - main + - staging + - develop + + +jobs: + build: + runs-on: ubuntu-latest + env: + working-directory: + backend + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Maven + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Build Docker backend-dev Image + run: docker-compose -f docker-compose.dev.yml build db-dev + + - name: Run Docker backend-dev Container + run: docker-compose -f docker-compose.dev.yml up -d db-dev + + - name: Run Tests + run: mvn clean test + + - name: Stop Docker backend-dev Container + run: docker-compose -f docker-compose.dev.yml down + + - name: Build Docker backend-prod Image + run: docker-compose -f docker-compose.yml build db + + - name: Run Docker backend-prod Container + run: docker-compose -f docker-compose.yml up -d db + + - name: Run Tests + run: mvn clean test + + - name: Stop Docker backend-prod Container + run: docker-compose -f docker-compose.yml down + + + From dfbf0df39b940ecd7ad1400643f420f7c83fa665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 02:31:15 +0300 Subject: [PATCH 02/74] fix(devops): change to relative path --- .github/workflows/backend_build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml index 238e0f94..14beec02 100644 --- a/.github/workflows/backend_build.yml +++ b/.github/workflows/backend_build.yml @@ -32,28 +32,28 @@ jobs: java-version: '17' - name: Build Docker backend-dev Image - run: docker-compose -f docker-compose.dev.yml build db-dev + run: docker-compose -f ../docker-compose.dev.yml build db-dev - name: Run Docker backend-dev Container - run: docker-compose -f docker-compose.dev.yml up -d db-dev + run: docker-compose -f ../docker-compose.dev.yml up -d db-dev - name: Run Tests run: mvn clean test - name: Stop Docker backend-dev Container - run: docker-compose -f docker-compose.dev.yml down + run: docker-compose -f ../docker-compose.dev.yml down - name: Build Docker backend-prod Image - run: docker-compose -f docker-compose.yml build db + run: docker-compose -f ../docker-compose.yml build db - name: Run Docker backend-prod Container - run: docker-compose -f docker-compose.yml up -d db + run: docker-compose -f ../docker-compose.yml up -d db - name: Run Tests run: mvn clean test - name: Stop Docker backend-prod Container - run: docker-compose -f docker-compose.yml down + run: docker-compose -f ../docker-compose.yml down From 290dcccbcd0b486282f8885bd241f0ac16c551b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 03:05:17 +0300 Subject: [PATCH 03/74] fix(devops): update docker version --- .github/workflows/backend_build.yml | 12 ++++++------ docker-compose.dev.yml | 2 +- docker-compose.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml index 14beec02..238e0f94 100644 --- a/.github/workflows/backend_build.yml +++ b/.github/workflows/backend_build.yml @@ -32,28 +32,28 @@ jobs: java-version: '17' - name: Build Docker backend-dev Image - run: docker-compose -f ../docker-compose.dev.yml build db-dev + run: docker-compose -f docker-compose.dev.yml build db-dev - name: Run Docker backend-dev Container - run: docker-compose -f ../docker-compose.dev.yml up -d db-dev + run: docker-compose -f docker-compose.dev.yml up -d db-dev - name: Run Tests run: mvn clean test - name: Stop Docker backend-dev Container - run: docker-compose -f ../docker-compose.dev.yml down + run: docker-compose -f docker-compose.dev.yml down - name: Build Docker backend-prod Image - run: docker-compose -f ../docker-compose.yml build db + run: docker-compose -f docker-compose.yml build db - name: Run Docker backend-prod Container - run: docker-compose -f ../docker-compose.yml up -d db + run: docker-compose -f docker-compose.yml up -d db - name: Run Tests run: mvn clean test - name: Stop Docker backend-prod Container - run: docker-compose -f ../docker-compose.yml down + run: docker-compose -f docker-compose.yml down diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index f13821e5..15b9d3da 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,4 +1,4 @@ -version: "3.1" +version: "3.3" name: bounswe2024group1-dev services: diff --git a/docker-compose.yml b/docker-compose.yml index 118c24ae..e2ad5bf6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.1" +version: "3.3" name: bounswe2024group1 # Production-like docker-compose setup From 8adb2859b623f01b97b403f2836d335e4dc55bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 22:41:05 +0300 Subject: [PATCH 04/74] fix(devops): delete name field in docker-compose.yml --- .github/workflows/backend_build.yml | 18 +++--------------- docker-compose.yml | 1 - 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml index 238e0f94..3c0c6c10 100644 --- a/.github/workflows/backend_build.yml +++ b/.github/workflows/backend_build.yml @@ -30,30 +30,18 @@ jobs: with: distribution: 'temurin' java-version: '17' - - - name: Build Docker backend-dev Image - run: docker-compose -f docker-compose.dev.yml build db-dev - - - name: Run Docker backend-dev Container - run: docker-compose -f docker-compose.dev.yml up -d db-dev - - - name: Run Tests - run: mvn clean test - - - name: Stop Docker backend-dev Container - run: docker-compose -f docker-compose.dev.yml down - name: Build Docker backend-prod Image - run: docker-compose -f docker-compose.yml build db + run: docker compose -f docker-compose.yml build db - name: Run Docker backend-prod Container - run: docker-compose -f docker-compose.yml up -d db + run: docker compose -f docker-compose.yml up -d db - name: Run Tests run: mvn clean test - name: Stop Docker backend-prod Container - run: docker-compose -f docker-compose.yml down + run: docker compose -f docker-compose.yml down diff --git a/docker-compose.yml b/docker-compose.yml index e2ad5bf6..c1564bd9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,4 @@ version: "3.3" -name: bounswe2024group1 # Production-like docker-compose setup services: From 00e33cae7a07af04339638b4bbfa632e1f33e037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 23:01:56 +0300 Subject: [PATCH 05/74] fix(devops): test in container --- .github/workflows/backend_build.yml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml index 3c0c6c10..902f79a9 100644 --- a/.github/workflows/backend_build.yml +++ b/.github/workflows/backend_build.yml @@ -24,24 +24,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - - name: Set up Maven - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - name: Build Docker backend-prod Image - run: docker compose -f docker-compose.yml build db - - - name: Run Docker backend-prod Container - run: docker compose -f docker-compose.yml up -d db - - - name: Run Tests - run: mvn clean test - - - name: Stop Docker backend-prod Container - run: docker compose -f docker-compose.yml down + - name: Test with Maven + run: docker compose -f docker-compose.dev.yml run --rm backend-dev mvn test From 1849dd7db41d4659266e5511ee114535fbd46461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Wed, 1 May 2024 23:08:50 +0300 Subject: [PATCH 06/74] fix(devops): revert version changes --- docker-compose.dev.yml | 2 +- docker-compose.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 15b9d3da..f13821e5 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: "3.1" name: bounswe2024group1-dev services: diff --git a/docker-compose.yml b/docker-compose.yml index c1564bd9..118c24ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,5 @@ -version: "3.3" +version: "3.1" +name: bounswe2024group1 # Production-like docker-compose setup services: From 06dbdb17eb8be5ecd2f683baff400a086793ed07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Mon, 6 May 2024 11:48:38 +0300 Subject: [PATCH 07/74] chore(devops): improve development compose, add more docs Added Compose Watch and mock API server to the development compose setup. Added more docs about the compose setup and common problems section. --- README.md | 53 ++++++++++++++++++++++++++----- docker-compose.yml => compose.yml | 3 -- dev.yml | 48 ++++++++++++++++++++++++++++ docker-compose.dev.yml | 51 ----------------------------- frontend/.dockerignore | 2 +- frontend/Dockerfile | 4 +-- frontend/Dockerfile.dev | 19 ++++++----- frontend/vite.config.ts | 11 ++++++- mock.yml | 10 ++++++ package.json | 7 ++++ 10 files changed, 134 insertions(+), 74 deletions(-) rename docker-compose.yml => compose.yml (96%) create mode 100644 dev.yml delete mode 100644 docker-compose.dev.yml create mode 100644 mock.yml create mode 100644 package.json diff --git a/README.md b/README.md index d2abe04d..6b0eeace 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is the repository for Group1 of SWE course in Spring'24. This is the repository for the Group1 of the Software Engineering course in Spring'24. The project is a web application that allows users to browse cuisines, dishes, and recipes. The application has a frontend, backend and mobile application. The frontend is a web application that allows users to browse cuisines and recipes. The backend is a REST API that serves the frontend and mobile application. The mobile application is a mobile application that allows users to browse cuisines and recipes. -Please refer to the Wiki for more information. +For common issues when developing, please check [Common Problems](#common-problems). Please refer to the Wiki for more information. For deploying from scratch (for third-parties), please refer to the [Deployment From Scratch](#deployment-from-scratch) section. @@ -35,16 +35,20 @@ You should have Docker installed. Each project has its own requirements however For deployment, you will need the [DigitalOcean CLI](https://docs.digitalocean.com/reference/doctl/how-to/install/) installed. +We also use Compose Watch for development. For that, your Docker version needs to be at least `2.20.0`. + ## Architecture We have `./frontend`, `./backend` and `./mobile` directories that each contain the respective codebase for the frontend, backend and mobile applications. Each codebase has its own readme file that explains how to get set up, run the application, and tests. In addition, it is recommended to use the docker-compose setup for development and local test of production builds. We utilize docker-compose to build and deploy the application as well. -In addition, we have our OpenAPI specification at `swagger/openapi.yml`. A Swagger UI instance is available in our docker compose setup at `localhost:8081`. +In addition, we have our OpenAPI specification at `swagger/openapi.yml`. A Swagger UI instance is available in our docker compose setup at `localhost:8081`. In the `mock.yml` file, we have a mock backend server that uses the API spec to generate fake responses. ### Docker Compose +All of the following docker commands are to be run from the root directory (not `frontend` or `backend`). + We have two docker compose setups: 1. (almost) identical to production. uses nginx to host the frontend at port 80, connect at `localhost:80`. Nginx reverse proxies `/api` to the backend at port 8080. @@ -52,16 +56,27 @@ We have two docker compose setups: To run the production-like setup, run: ```bash -docker-compose up -d +docker compose up -d ``` 2. Development mode. uses the vite development server at port 5173 and exposes it at the same port. This setup will run a development setup that hot reloads the frontend code. The backend code is also run using `mvn spring-boot:run` which means it will reload on recompile. For further detail, refer to the backend README. ```bash -docker-compose -f docker-compose.dev.yml up -d +docker compose -f dev.yml up --watch +``` + +In addition, you can choose to use the mock API server which will create a mock API server using our API spec. This is useful for frontend development to continue when the backend is lagging behind or not available for some reason. + +```bash +docker compose -f dev.yml -f mock.yml up --watch ``` -With the hot reloading setup, Vite dev server will hot reload all your changes, however you'll need to restart if you make any changes to package.json as the `node_modules` are fetched as part of the start command. +> [!TIP] +> Note that the `-d` and `--watch` flags can't be combined. If you'd like to run the compose setup detached, you will have to separately run Compose watch using `docker compose -f dev.yml watch`. This is needed for hot-reloading on the frontend. + +With the hot reloading setup, Vite dev server will hot reload all your changes, and the image will be rebuilt when `package.json` changes. + +To take down the compose setups, you can do a Ctrl+C in the terminal for the development setup or do `docker compose down`. If you're running the development setup in detached mode, you can do `docker compose -f dev.yml down`. ### Development @@ -85,15 +100,18 @@ doctl registry login ### Deploying the Application +> [!IMPORTANT] +> Note that you need to use `docker compose` and NOT `docker-compose`. Dashed version is the older version that uses Python and may have conflicts with your local Python environment. We also don't provide `version` which may cause an error with the old Compose V1. For more details, please check [the Docker documentation](https://docs.docker.com/compose/intro/history/). + When you're going to deploy only the application (no changes to infrastructre), you can follow the steps below: -1. Build images using the docker-compose setup. +1. Build images using the docker compose setup. ```bash -docker-compose build +docker compose build ``` -2. Tag images for the registry (assuming your project folder is `bounswe2024group1`). +2. Tag images for the registry. ```bash # for prod @@ -199,3 +217,22 @@ doctl apps create --spec .do/app-staging.yml ``` This will create the app and deploy it to the DigitalOcean App Platform. You can now follow the steps above in the normal deployment process to push images and deploy your first instance. + +#### Common Problems + +##### Compose fails at "yarn install --immutable" + +- If the error has "lockfile would have been modified by this install, which is explicitly forbidden". + +The `yarn.lock` file is forbidden from changing within the Docker build. This ensures that the same packages are used locally as the ones used inside the Docker image. Solution: do a `yarn install` in the `frontend/` folder. + +##### "port is already allocated" + +You have something running that is listening on one of the ports of our services. Common reasons include: + +- You have the production or the development compose setup running and are trying to run the other one: Check what is running using `docker ps`, if you see other containers that are binding a required port, take them down using the appropriate docker compose command. +- You have another application that is binding a required port. For now, we're binding `8081`, `8080` and `80` for the production setup; and `8081` and `5173` for the development setup. + +##### "no such file or directory" + +Make sure you run all docker compose commands in the root folder. Make sure you run all `yarn` commands in the `frontend` folder. diff --git a/docker-compose.yml b/compose.yml similarity index 96% rename from docker-compose.yml rename to compose.yml index 118c24ae..fd7d546d 100644 --- a/docker-compose.yml +++ b/compose.yml @@ -1,4 +1,3 @@ -version: "3.1" name: bounswe2024group1 # Production-like docker-compose setup @@ -16,8 +15,6 @@ services: environment: MYSQL_DATABASE: "cuisines-test" MYSQL_ROOT_PASSWORD: "admin" - ports: - - "3306:3306" volumes: - db-data:/var/lib/mysql backend: diff --git a/dev.yml b/dev.yml new file mode 100644 index 00000000..1a51075f --- /dev/null +++ b/dev.yml @@ -0,0 +1,48 @@ +name: bounswe2024group1-dev + +services: + swagger: + extends: + file: compose.yml + service: swagger + db: + extends: + file: compose.yml + service: db + volumes: + - db-data:/var/lib/mysql + backend: + build: + context: ./backend + dockerfile: ./Dockerfile.dev + platform: linux/amd64 + volumes: + - ./backend:/app + environment: + DB_HOST: db + DB_NAME: cuisines-test + DB_USER: root + DB_PASSWORD: admin + SPRING_PROFILES_ACTIVE: docker + depends_on: + - db + web: + build: + context: ./frontend + dockerfile: ./Dockerfile.dev + environment: + - PROXY_API=http://backend:8080 + ports: + - "5173:5173" + develop: + watch: + - action: sync + path: ./frontend + target: /app + ignore: + - ./frontend/node_modules/ + - action: rebuild + path: ./frontend/package.json +volumes: + node_modules: + db-data: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index f13821e5..00000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,51 +0,0 @@ -version: "3.1" -name: bounswe2024group1-dev - -services: - swagger-dev: - image: swaggerapi/swagger-ui - environment: - SWAGGER_JSON: /openapi.yaml - volumes: - - ./swagger/openapi.yml:/openapi.yaml - ports: - - "8081:8080" - db-dev: - image: mysql:latest - environment: - MYSQL_DATABASE: "cuisines-test" - MYSQL_ROOT_PASSWORD: "admin" - ports: - - "3306:3306" - volumes: - - db-data-dev:/var/lib/mysql - backend-dev: - build: - context: ./backend - dockerfile: ./Dockerfile.dev - platform: linux/amd64 - volumes: - - ./backend:/app - environment: - DB_HOST: db-dev - DB_NAME: cuisines-test - DB_USER: root - DB_PASSWORD: admin - SPRING_PROFILES_ACTIVE: docker - depends_on: - - db-dev - web-dev: - build: - context: ./frontend - dockerfile: ./Dockerfile.dev - environment: - - PROXY_API=http://backend-dev:8080 - ports: - - "5173:5173" - volumes: - - node_modules:/app/node_modules - - ./frontend:/app - -volumes: - node_modules: - db-data-dev: diff --git a/frontend/.dockerignore b/frontend/.dockerignore index a6855144..4bf174ca 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,5 +1,5 @@ # installed by Dockerfile -node_modules +node_modules/ # build output dist diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b25dae5d..9701602a 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -2,8 +2,8 @@ FROM node:18-alpine AS build WORKDIR /app # cache package installs -COPY yarn.lock package.json /app/ -RUN yarn install +COPY .yarnrc.yml yarn.lock package.json /app/ +RUN yarn install --immutable COPY . /app RUN yarn build diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index bb53ffd4..b3ccdaea 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,14 +1,17 @@ FROM node:18-alpine +RUN adduser -D -s /bin/sh -u 1001 app +RUN corepack enable +USER app + WORKDIR /app +COPY --chown=app:app .yarnrc.yml package.json yarn.lock . +RUN yarn install -# expect a bind mount to be mounted at /app -EXPOSE 5173 -VOLUME /app/node_modules +# Copy source files into application directory +COPY --chown=app:app . /app/ -RUN corepack enable +EXPOSE 5173 -# we can't pack dependencies or bind them as -# the platform of the host and the container -# are not the same. -CMD yarn && yarn dev --host 0.0.0.0 --strictPort --force +# We use Compose in watch mode. +CMD yarn dev --host 0.0.0.0 --strictPort --force diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 7bf188cd..85d46793 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,8 +2,12 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import path from "path"; -// https://vitejs.dev/config/ const PROXY_API = process.env.PROXY_API; + +// Removes /api/v1 from proxied paths +const PROXY_REMOVE_BASE_PATH = !!process.env.PROXY_REMOVE_BASE_PATH; + +// https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], resolve: { @@ -16,6 +20,11 @@ export default defineConfig({ ? { "/api": { target: PROXY_API, + rewrite(path) { + if (PROXY_REMOVE_BASE_PATH) { + return path.replace("/api/v1", ""); + } else return path; + }, }, } : {}, diff --git a/mock.yml b/mock.yml new file mode 100644 index 00000000..7f3f5f4b --- /dev/null +++ b/mock.yml @@ -0,0 +1,10 @@ +services: + backend: + build: !reset null + image: stoplight/prism + volumes: + - ./swagger/openapi.yml:/usr/src/prism/spec.yml + command: "mock /usr/src/prism/spec.yml -h 0.0.0.0 -p 8080" + web: + environment: + - PROXY_REMOVE_BASE_PATH=1 diff --git a/package.json b/package.json new file mode 100644 index 00000000..264a2575 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "engines": { + "yarn": "run-commands-in-the-frontend-folder", + "npm": "run-commands-in-the-frontend-folder", + "node": "run-commands-in-the-frontend-folder" + } +} From 8e5e3ca2ecac2cc3b29bdf6dd9de4b7ce8ddd76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Sun, 5 May 2024 21:22:41 +0300 Subject: [PATCH 08/74] fix(frontend): fix hanging tests by switching to happy-dom --- frontend/.nvmrc | 2 +- frontend/Dockerfile | 2 +- frontend/Dockerfile.dev | 5 +- frontend/README.md | 4 +- frontend/package.json | 6 + frontend/src/App.test.tsx | 22 +- frontend/src/components/SearchBar.test.tsx | 33 + frontend/src/routes/home.test.tsx | 20 - frontend/tests/setup.ts | 24 +- frontend/tsconfig.json | 2 +- frontend/vitest.config.ts | 2 +- frontend/yarn.lock | 1582 +++++++++++++++++--- 12 files changed, 1467 insertions(+), 237 deletions(-) create mode 100644 frontend/src/components/SearchBar.test.tsx diff --git a/frontend/.nvmrc b/frontend/.nvmrc index fb3e6603..a81debae 100644 --- a/frontend/.nvmrc +++ b/frontend/.nvmrc @@ -1 +1 @@ -v18.20.2 +v20.12.2 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 9701602a..ea921ef7 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine AS build +FROM node:20-alpine AS build WORKDIR /app # cache package installs diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index b3ccdaea..cd2d4b1b 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:18-alpine +FROM node:20-alpine RUN adduser -D -s /bin/sh -u 1001 app RUN corepack enable USER app @@ -13,5 +13,6 @@ COPY --chown=app:app . /app/ EXPOSE 5173 -# We use Compose in watch mode. +# We use Compose in watch mode which syncs files and +# rebuilds on package.json changes CMD yarn dev --host 0.0.0.0 --strictPort --force diff --git a/frontend/README.md b/frontend/README.md index b7492783..18c98653 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -2,12 +2,14 @@ This project uses the Vite template for React with TypeScript. -Before you get started, you'll need to have Node.js (v18.20.1) installed and you'll need to enable corepack: +Before you get started, you'll need to have Node.js (v20.12.1) installed and you'll need to enable corepack: ```bash corepack enable ``` +() + After that, you can install packages using ```bash diff --git a/frontend/package.json b/frontend/package.json index b3846f6c..f3dd28d9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,6 +3,9 @@ "private": true, "version": "0.0.0", "type": "module", + "engines": { + "node": ">=20.12.2" + }, "scripts": { "start": "vite preview", "dev": "vite", @@ -23,6 +26,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "country-emoji": "^1.5.6", + "cypress": "^13.8.1", "lucide-react": "^0.376.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -41,11 +45,13 @@ "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.5.0", + "@vitest/ui": "^1.5.3", "autoprefixer": "^10.4.19", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", + "happy-dom": "^14.7.1", "husky": "^9.0.11", "jsdom": "^24.0.0", "lint-staged": "^15.2.2", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index fd3bbc28..a04483d0 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -1,9 +1,17 @@ -import { test, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { routeConfig } from "./routes"; +import { RouterProvider, createMemoryRouter } from "react-router-dom"; +import { expect, test } from "vitest"; -vi.mock("@/services/search", () => { - return { - searchDishes: () => Promise.resolve([]), - }; -}); +test("welcome test is shown", () => { + // Arrange + const router = createMemoryRouter(routeConfig, { + initialEntries: ["/"], + }); + + render(); -test("empty", () => {}); + // Act + const welcomeText = screen.getByText("Welcome to Semantic Browse"); + expect(welcomeText).toBeInTheDocument(); +}); diff --git a/frontend/src/components/SearchBar.test.tsx b/frontend/src/components/SearchBar.test.tsx new file mode 100644 index 00000000..139fcf0b --- /dev/null +++ b/frontend/src/components/SearchBar.test.tsx @@ -0,0 +1,33 @@ +import { RouterProvider, createMemoryRouter } from "react-router-dom"; +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { routeConfig } from "../routes"; +import { expect, test, vi } from "vitest"; + +vi.mock("@/services/search", () => { + return { + searchDishes: () => Promise.resolve([]), + }; +}); + +test("searching something goes to /search", async () => { + // Arrange + const router = createMemoryRouter(routeConfig, { + initialEntries: ["/"], + }); + + render(); + + // Act + const search = screen.getAllByPlaceholderText("Search for dishes...")[0]; + screen.debug(search); + fireEvent.change(search, { target: { value: "hello" } }); + + const button = screen.getAllByRole("button", { name: /search/i })[0]; + screen.debug(button); + fireEvent.click(button); + + // Assert + await waitFor(() => { + expect(router.state.location.pathname).toBe("/search"); + }); +}); diff --git a/frontend/src/routes/home.test.tsx b/frontend/src/routes/home.test.tsx index 9612e510..8c3e6037 100644 --- a/frontend/src/routes/home.test.tsx +++ b/frontend/src/routes/home.test.tsx @@ -9,26 +9,6 @@ vi.mock("@/services/search", () => { }; }); -test("searching something goes to /search", async () => { - // Arrange - const router = createMemoryRouter(routeConfig, { - initialEntries: ["/"], - }); - - render(); - - // Act - const search = screen.getAllByPlaceholderText("Search for dishes...")[0]; - fireEvent.change(search, { target: { value: "hello" } }); - const button = screen.getAllByText("Search")[0]; - fireEvent.click(button); - - // Assert - await waitFor(() => { - expect(router.state.location.pathname).toBe("/search"); - }); -}); - test("log in button goes to /login", async () => { // Arrange const router = createMemoryRouter(routeConfig, { diff --git a/frontend/tests/setup.ts b/frontend/tests/setup.ts index 99324505..3081b334 100644 --- a/frontend/tests/setup.ts +++ b/frontend/tests/setup.ts @@ -1,8 +1,6 @@ -import { expect, afterEach, vi } from "vitest"; +import { afterEach, vi } from "vitest"; import { cleanup } from "@testing-library/react"; -import * as matchers from "@testing-library/jest-dom/matchers"; - -expect.extend(matchers); +import "@testing-library/jest-dom/vitest"; vi.mock("zustand/middleware", () => ({ persist: (a: unknown) => a, @@ -12,3 +10,21 @@ vi.mock("zustand/middleware", () => ({ afterEach(() => { cleanup(); }); + +// happy-dom doesn't support submit events on buttons, so we need to +// dispatch a submit event when a button is clicked +const originalDispatchEvent = HTMLElement.prototype.dispatchEvent; +HTMLElement.prototype.dispatchEvent = function (event): boolean { + const result = originalDispatchEvent.call(this, event); + + if ( + event.type === "click" && + this.tagName === "BUTTON" && + this.getAttribute("type") === "submit" + ) { + this.dispatchEvent( + new Event("submit", { bubbles: true, cancelable: true }), + ); + } + return result; +}; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 393cc80c..ee4d3003 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -27,6 +27,6 @@ "@/*": ["./src/*"] } }, - "include": ["src"], + "include": ["src", "tests/setup.ts"], "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 83d91a16..e4aeadb2 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -7,7 +7,7 @@ export default mergeConfig( test: { setupFiles: ["./tests/setup.ts"], globals: true, - environment: "jsdom", + environment: "happy-dom", }, }), ); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 11eeb3db..1e6ad33a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -29,40 +29,74 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-validator-identifier@npm:7.22.20" - checksum: 10c0/dcad63db345fb110e032de46c3688384b0008a42a4845180ce7cd62b1a9c0507a1bed727c4d1060ed1a03ae57b4d918570259f81724aaac1a5b776056f37504e +"@babel/helper-validator-identifier@npm:^7.24.5": + version: 7.24.5 + resolution: "@babel/helper-validator-identifier@npm:7.24.5" + checksum: 10c0/05f957229d89ce95a137d04e27f7d0680d84ae48b6ad830e399db0779341f7d30290f863a93351b4b3bde2166737f73a286ea42856bb07c8ddaa95600d38645c languageName: node linkType: hard "@babel/highlight@npm:^7.24.2": - version: 7.24.2 - resolution: "@babel/highlight@npm:7.24.2" + version: 7.24.5 + resolution: "@babel/highlight@npm:7.24.5" dependencies: - "@babel/helper-validator-identifier": "npm:^7.22.20" + "@babel/helper-validator-identifier": "npm:^7.24.5" chalk: "npm:^2.4.2" js-tokens: "npm:^4.0.0" picocolors: "npm:^1.0.0" - checksum: 10c0/98ce00321daedeed33a4ed9362dc089a70375ff1b3b91228b9f05e6591d387a81a8cba68886e207861b8871efa0bc997ceabdd9c90f6cce3ee1b2f7f941b42db + checksum: 10c0/e98047d3ad24608bfa596d000c861a2cc875af897427f2833b91a4e0d4cead07301a7ec15fa26093dcd61e036e2eed2db338ae54f93016fe0dc785fadc4159db languageName: node linkType: hard -"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.24.1": - version: 7.24.4 - resolution: "@babel/runtime@npm:7.24.4" +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.9.2": + version: 7.24.5 + resolution: "@babel/runtime@npm:7.24.5" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/785aff96a3aa8ff97f90958e1e8a7b1d47f793b204b47c6455eaadc3f694f48c97cd5c0a921fe3596d818e71f18106610a164fb0f1c71fd68c622a58269d537c + checksum: 10c0/05730e43e8ba6550eae9fd4fb5e7d9d3cb91140379425abcb2a1ff9cebad518a280d82c4c4b0f57ada26a863106ac54a748d90c775790c0e2cd0ddd85ccdf346 languageName: node linkType: hard -"@babel/runtime@npm:^7.9.2": - version: 7.24.5 - resolution: "@babel/runtime@npm:7.24.5" +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: 10c0/eb42729851adca56d19a08e48d5a1e95efd2a32c55ae0323de8119052be0510d4b7a1611f2abcbf28c044a6c11e6b7d38f99fccdad7429300c37a8ea5fb95b44 + languageName: node + linkType: hard + +"@cypress/request@npm:^3.0.0": + version: 3.0.1 + resolution: "@cypress/request@npm:3.0.1" + dependencies: + aws-sign2: "npm:~0.7.0" + aws4: "npm:^1.8.0" + caseless: "npm:~0.12.0" + combined-stream: "npm:~1.0.6" + extend: "npm:~3.0.2" + forever-agent: "npm:~0.6.1" + form-data: "npm:~2.3.2" + http-signature: "npm:~1.3.6" + is-typedarray: "npm:~1.0.0" + isstream: "npm:~0.1.2" + json-stringify-safe: "npm:~5.0.1" + mime-types: "npm:~2.1.19" + performance-now: "npm:^2.1.0" + qs: "npm:6.10.4" + safe-buffer: "npm:^5.1.2" + tough-cookie: "npm:^4.1.3" + tunnel-agent: "npm:^0.6.0" + uuid: "npm:^8.3.2" + checksum: 10c0/8eb92a665e6549e2533f5169431addcaad0307f51a8c7f3b6b169eb79b4d673373784a527590a47b0a2905ad5f601b24ab2d1b31d184243235aba470ffc9c1f7 + languageName: node + linkType: hard + +"@cypress/xvfb@npm:^1.2.4": + version: 1.2.4 + resolution: "@cypress/xvfb@npm:1.2.4" dependencies: - regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/05730e43e8ba6550eae9fd4fb5e7d9d3cb91140379425abcb2a1ff9cebad518a280d82c4c4b0f57ada26a863106ac54a748d90c775790c0e2cd0ddd85ccdf346 + debug: "npm:^3.1.0" + lodash.once: "npm:^4.1.1" + checksum: 10c0/1bf6224b244f6093033d77f04f6bef719280542656de063cf8ac3f38957b62aa633e6918af0b9673a8bf0123b42a850db51d9729a3ae3da885ac179bc7fc1d26 languageName: node linkType: hard @@ -453,6 +487,13 @@ __metadata: languageName: node linkType: hard +"@polka/url@npm:^1.0.0-next.24": + version: 1.0.0-next.25 + resolution: "@polka/url@npm:1.0.0-next.25" + checksum: 10c0/ef61f0a0fe94bb6e1143fc5b9d5a12e6ca9dbd2c57843ebf81db432c21b9f1005c09e8a1ef8b6d5ddfa42146ca65b640feb2d353bd0d3546da46ba59e48a5349 + languageName: node + linkType: hard + "@radix-ui/primitive@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/primitive@npm:1.0.1" @@ -1034,114 +1075,114 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.17.1" +"@rollup/rollup-android-arm-eabi@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.17.2" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-android-arm64@npm:4.17.1" +"@rollup/rollup-android-arm64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-android-arm64@npm:4.17.2" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-darwin-arm64@npm:4.17.1" +"@rollup/rollup-darwin-arm64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.17.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-darwin-x64@npm:4.17.1" +"@rollup/rollup-darwin-x64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.17.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.17.1" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.17.1" +"@rollup/rollup-linux-arm-musleabihf@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.17.2" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.17.1" +"@rollup/rollup-linux-arm64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.17.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.17.1" +"@rollup/rollup-linux-arm64-musl@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.17.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.1" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.17.1" +"@rollup/rollup-linux-riscv64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.17.2" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.17.1" +"@rollup/rollup-linux-s390x-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.17.2" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.17.1" +"@rollup/rollup-linux-x64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.17.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.17.1" +"@rollup/rollup-linux-x64-musl@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.17.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.17.1" +"@rollup/rollup-win32-arm64-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.17.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.17.1" +"@rollup/rollup-win32-ia32-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.17.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.17.1": - version: 4.17.1 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.17.1" +"@rollup/rollup-win32-x64-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.17.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1369,7 +1410,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.12.7": +"@types/node@npm:*, @types/node@npm:^20.12.7": version: 20.12.7 resolution: "@types/node@npm:20.12.7" dependencies: @@ -1411,15 +1452,38 @@ __metadata: languageName: node linkType: hard +"@types/sinonjs__fake-timers@npm:8.1.1": + version: 8.1.1 + resolution: "@types/sinonjs__fake-timers@npm:8.1.1" + checksum: 10c0/e2e6c425a548177c0930c2f9b82d3951956c9701b9ebf59623d5ad2c3229c523d3c0d598e79fe7392a239657abd3dbe3676be0650ce438bcd1199ee3b617a4d7 + languageName: node + linkType: hard + +"@types/sizzle@npm:^2.3.2": + version: 2.3.8 + resolution: "@types/sizzle@npm:2.3.8" + checksum: 10c0/ab5460147ae6680cc20c2223a8f17d9f7c97144b70f00a222a1c32d68b5207696d48177ab9784dda88c74d93ed5a78dd31f74d271b15382520b423c81b4aac89 + languageName: node + linkType: hard + +"@types/yauzl@npm:^2.9.1": + version: 2.10.3 + resolution: "@types/yauzl@npm:2.10.3" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/f1b7c1b99fef9f2fe7f1985ef7426d0cebe48cd031f1780fcdc7451eec7e31ac97028f16f50121a59bcf53086a1fc8c856fd5b7d3e00970e43d92ae27d6b43dc + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^7.2.0": - version: 7.7.1 - resolution: "@typescript-eslint/eslint-plugin@npm:7.7.1" + version: 7.8.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.8.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:7.7.1" - "@typescript-eslint/type-utils": "npm:7.7.1" - "@typescript-eslint/utils": "npm:7.7.1" - "@typescript-eslint/visitor-keys": "npm:7.7.1" + "@typescript-eslint/scope-manager": "npm:7.8.0" + "@typescript-eslint/type-utils": "npm:7.8.0" + "@typescript-eslint/utils": "npm:7.8.0" + "@typescript-eslint/visitor-keys": "npm:7.8.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" @@ -1432,44 +1496,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/11a085240e7daf4bdeb011aa53ac7cfeea6263c60d53607823f5c314eb5c9d559b28fce0d6686acb9702ee3d0cb0406534fafae61163e5a903eaf818c48194ad + checksum: 10c0/37ca22620d1834ff0baa28fa4b8fd92039a3903cb95748353de32d56bae2a81ce50d1bbaed27487eebc884e0a0f9387fcb0f1647593e4e6df5111ef674afa9f0 languageName: node linkType: hard "@typescript-eslint/parser@npm:^7.2.0": - version: 7.7.1 - resolution: "@typescript-eslint/parser@npm:7.7.1" + version: 7.8.0 + resolution: "@typescript-eslint/parser@npm:7.8.0" dependencies: - "@typescript-eslint/scope-manager": "npm:7.7.1" - "@typescript-eslint/types": "npm:7.7.1" - "@typescript-eslint/typescript-estree": "npm:7.7.1" - "@typescript-eslint/visitor-keys": "npm:7.7.1" + "@typescript-eslint/scope-manager": "npm:7.8.0" + "@typescript-eslint/types": "npm:7.8.0" + "@typescript-eslint/typescript-estree": "npm:7.8.0" + "@typescript-eslint/visitor-keys": "npm:7.8.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/ace43eeb8123bbee61e936650e1d57a2cf70f2030870c6dcad8602fce3f7cdf2cce350121dbbc66cffd60bac36652f426a1c5293c45ed28998b90cd95673b5c9 + checksum: 10c0/0dd994c1b31b810c25e1b755b8d352debb7bf21a31f9a91acaec34acf4e471320bcceaa67cf64c110c0b8f5fac10a037dbabac6ec423e17adf037e59a7bce9c1 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/scope-manager@npm:7.7.1" +"@typescript-eslint/scope-manager@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/scope-manager@npm:7.8.0" dependencies: - "@typescript-eslint/types": "npm:7.7.1" - "@typescript-eslint/visitor-keys": "npm:7.7.1" - checksum: 10c0/4032da8fce8922044a6b659c8435ba203377778d5b7de6a5572c1172f2e3cf8ddd890a0f9e083c5d5315a9c2dba323707528ee4ad3cc1be2bd334de2527ef5cb + "@typescript-eslint/types": "npm:7.8.0" + "@typescript-eslint/visitor-keys": "npm:7.8.0" + checksum: 10c0/c253b98e96d4bf0375f473ca2c4d081726f1fd926cdfa65ee14c9ee99cca8eddb763b2d238ac365daa7246bef21b0af38180d04e56e9df7443c0e6f8474d097c languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/type-utils@npm:7.7.1" +"@typescript-eslint/type-utils@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/type-utils@npm:7.8.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.7.1" - "@typescript-eslint/utils": "npm:7.7.1" + "@typescript-eslint/typescript-estree": "npm:7.8.0" + "@typescript-eslint/utils": "npm:7.8.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependencies: @@ -1477,23 +1541,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/bd083c4106e207aa8c2a71251eca52d23c7ea905399b8c62004f3bb1e85b9c88d601db9dcecae88beef0f8362d53450bb2721aab353ee731c1665496fea3fbda + checksum: 10c0/00f6315626b64f7dbc1f7fba6f365321bb8d34141ed77545b2a07970e59a81dbdf768c1e024225ea00953750d74409ddd8a16782fc4a39261e507c04192dacab languageName: node linkType: hard -"@typescript-eslint/types@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/types@npm:7.7.1" - checksum: 10c0/7d240503d9d0b12d68c8204167690609f02ededb77dcb035c1c8b932da08cf43553829c29a5f7889824a7337463c300343bc5abe532479726d4c83443a7e2704 +"@typescript-eslint/types@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/types@npm:7.8.0" + checksum: 10c0/b2fdbfc21957bfa46f7d8809b607ad8c8b67c51821d899064d09392edc12f28b2318a044f0cd5d523d782e84e8f0558778877944964cf38e139f88790cf9d466 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/typescript-estree@npm:7.7.1" +"@typescript-eslint/typescript-estree@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.8.0" dependencies: - "@typescript-eslint/types": "npm:7.7.1" - "@typescript-eslint/visitor-keys": "npm:7.7.1" + "@typescript-eslint/types": "npm:7.8.0" + "@typescript-eslint/visitor-keys": "npm:7.8.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -1503,34 +1567,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/c6b32bd96fd13b9da0a30de01935066f7505f6214f5759e3cd019f7d1852f7bf19358765f62e51de72be47647656aa0e8f07ac0ab316c4149a4e6bd1dd12cbb6 + checksum: 10c0/1690b62679685073dcb0f62499f0b52b445b37ae6e12d02aa4acbafe3fb023cf999b01f714b6282e88f84fd934fe3e2eefb21a64455d19c348d22bbc68ca8e47 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/utils@npm:7.7.1" +"@typescript-eslint/utils@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/utils@npm:7.8.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.15" "@types/semver": "npm:^7.5.8" - "@typescript-eslint/scope-manager": "npm:7.7.1" - "@typescript-eslint/types": "npm:7.7.1" - "@typescript-eslint/typescript-estree": "npm:7.7.1" + "@typescript-eslint/scope-manager": "npm:7.8.0" + "@typescript-eslint/types": "npm:7.8.0" + "@typescript-eslint/typescript-estree": "npm:7.8.0" semver: "npm:^7.6.0" peerDependencies: eslint: ^8.56.0 - checksum: 10c0/0986b8c297d6bfdbd2ac8cd3bcf447ef9b934e2dae536771d3368a5c284a0b16c0ea041f82aa100c48d05acc33198e1a3d9d721d3319ae80abba0f5e69c21633 + checksum: 10c0/31fb58388d15b082eb7bd5bce889cc11617aa1131dfc6950471541b3df64c82d1c052e2cccc230ca4ae80456d4f63a3e5dccb79899a8f3211ce36c089b7d7640 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.7.1": - version: 7.7.1 - resolution: "@typescript-eslint/visitor-keys@npm:7.7.1" +"@typescript-eslint/visitor-keys@npm:7.8.0": + version: 7.8.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.8.0" dependencies: - "@typescript-eslint/types": "npm:7.7.1" + "@typescript-eslint/types": "npm:7.8.0" eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/19cbd14ac9a234d847f457cbd880cbd98b83c331a46d2dc2d8c0e6cb54ce6159552f6dd2f7236035be1a71f13f48df4a2aa09e70ad1f1e2ff3da7c3622927bd3 + checksum: 10c0/5892fb5d9c58efaf89adb225f7dbbb77f9363961f2ff420b6b130bdd102dddd7aa8a16c46a5a71c19889d27b781e966119a89270555ea2cb5653a04d8994123d languageName: node linkType: hard @@ -1552,57 +1616,74 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:1.5.2": - version: 1.5.2 - resolution: "@vitest/expect@npm:1.5.2" +"@vitest/expect@npm:1.5.3": + version: 1.5.3 + resolution: "@vitest/expect@npm:1.5.3" dependencies: - "@vitest/spy": "npm:1.5.2" - "@vitest/utils": "npm:1.5.2" + "@vitest/spy": "npm:1.5.3" + "@vitest/utils": "npm:1.5.3" chai: "npm:^4.3.10" - checksum: 10c0/4e8400a55f9e2d4a5a5e2be8c5679fb0a3bfa7550085597b4022320bd9d8f70663707bb4ea02a5403b5327f405d7bc3de5dd21bbf43a8aabc6ceecfc83760ba8 + checksum: 10c0/38f4419ced67931d0dffb31cfc1d0e1efaf53927a373ed3d371bf8e4de40a962100438f63058e4ca8581c72a3892315ba3be6d5718109df9a559422cec212770 languageName: node linkType: hard -"@vitest/runner@npm:1.5.2": - version: 1.5.2 - resolution: "@vitest/runner@npm:1.5.2" +"@vitest/runner@npm:1.5.3": + version: 1.5.3 + resolution: "@vitest/runner@npm:1.5.3" dependencies: - "@vitest/utils": "npm:1.5.2" + "@vitest/utils": "npm:1.5.3" p-limit: "npm:^5.0.0" pathe: "npm:^1.1.1" - checksum: 10c0/9fc3e668817ceb49785a366959281eee31739b393b7629d19534a0f3044558663130275cf0631c1821cdf916b491bc3e8b57da1cfaeefa66a99473c08c4e7621 + checksum: 10c0/7d0021b2b2cb173be6cb9b5a4d150dcae5194bc7d14a3df1b69e752c691ecebf638ee856b78b8f85cc55925793dbb101c5797af94535cbdbf70da60101786d85 languageName: node linkType: hard -"@vitest/snapshot@npm:1.5.2": - version: 1.5.2 - resolution: "@vitest/snapshot@npm:1.5.2" +"@vitest/snapshot@npm:1.5.3": + version: 1.5.3 + resolution: "@vitest/snapshot@npm:1.5.3" dependencies: magic-string: "npm:^0.30.5" pathe: "npm:^1.1.1" pretty-format: "npm:^29.7.0" - checksum: 10c0/67ae235e59f1adec1e6a44e2a8ea1d2ee98f2416d3ccf566798474ee98943b87b3f20c2098e193866b01780ae4c767239e4f94dbbb3daf0f0978b8106e8e86b1 + checksum: 10c0/9f143290c2208847290c49f59a3869f4b0229a5c9770fe612cc9c1e103eb5e8c80cdc5803cf1bb3e22900a3899d45e9979cf2b891080b0c3a85a346964425b8b languageName: node linkType: hard -"@vitest/spy@npm:1.5.2": - version: 1.5.2 - resolution: "@vitest/spy@npm:1.5.2" +"@vitest/spy@npm:1.5.3": + version: 1.5.3 + resolution: "@vitest/spy@npm:1.5.3" dependencies: tinyspy: "npm:^2.2.0" - checksum: 10c0/d66de2873f762ba9a45ca03fe9d11db0c1910635e02289a5b54746c41bd68366b9d198b265e13a8608cc21774f4f197afb25acbba5c0769e651598df0eaab5b3 + checksum: 10c0/d69e735d377b9370eccb8ae3f6d51e9540fbc7b2f05ecc2158e2e2a2793237481cff629840f61394266e2dda5b30069880c658cca1430197781c845364d509b0 + languageName: node + linkType: hard + +"@vitest/ui@npm:^1.5.3": + version: 1.5.3 + resolution: "@vitest/ui@npm:1.5.3" + dependencies: + "@vitest/utils": "npm:1.5.3" + fast-glob: "npm:^3.3.2" + fflate: "npm:^0.8.1" + flatted: "npm:^3.2.9" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + sirv: "npm:^2.0.4" + peerDependencies: + vitest: 1.5.3 + checksum: 10c0/20a41c6204394e6d6817eb6c37e7eb216a6e401ed52d5c9d287a3b8118a2f92893afe46fcfe6de378127dec2660f8b90e3379e9754c907c35c9363b62f13ff23 languageName: node linkType: hard -"@vitest/utils@npm:1.5.2": - version: 1.5.2 - resolution: "@vitest/utils@npm:1.5.2" +"@vitest/utils@npm:1.5.3": + version: 1.5.3 + resolution: "@vitest/utils@npm:1.5.3" dependencies: diff-sequences: "npm:^29.6.3" estree-walker: "npm:^3.0.3" loupe: "npm:^2.3.7" pretty-format: "npm:^29.7.0" - checksum: 10c0/019eeac016deb5d2434db4990b2ba1ebf37434a020c3d6bcf163c77af80e01dd9add7b3d379bdacd91497847026a5bd2aafe393035c48f5296ce5275bad00126 + checksum: 10c0/1d5a1717f5d43e111c865b4dc8b1844ee3c1a858f87f3959f4ed321fa1c4affbd3a9cf1729215859c08bc3248559020fa5388bad9814000fd20d474c3c711db5 languageName: node linkType: hard @@ -1669,6 +1750,22 @@ __metadata: languageName: node linkType: hard +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9 + languageName: node + linkType: hard + +"ansi-escapes@npm:^4.3.0": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: "npm:^0.21.3" + checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 + languageName: node + linkType: hard + "ansi-escapes@npm:^6.2.0": version: 6.2.1 resolution: "ansi-escapes@npm:6.2.1" @@ -1739,6 +1836,13 @@ __metadata: languageName: node linkType: hard +"arch@npm:^2.2.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: 10c0/4ceaf8d8207817c216ebc4469742052cb0a097bc45d9b7fcd60b7507220da545a28562ab5bdd4dfe87921bb56371a0805da4e10d704e01f93a15f83240f1284c + languageName: node + linkType: hard + "arg@npm:^5.0.2": version: 5.0.2 resolution: "arg@npm:5.0.2" @@ -1778,6 +1882,22 @@ __metadata: languageName: node linkType: hard +"asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: "npm:~2.1.0" + checksum: 10c0/00c8a06c37e548762306bcb1488388d2f76c74c36f70c803f0c081a01d3bdf26090fc088cd812afc5e56a6d49e33765d451a5f8a68ab9c2b087eba65d2e980e0 + languageName: node + linkType: hard + +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 10c0/b194b9d50c3a8f872ee85ab110784911e696a4d49f7ee6fc5fb63216dedbefd2c55999c70cb2eaeb4cf4a0e0338b44e9ace3627117b5bf0d42460e9132f21b91 + languageName: node + linkType: hard + "assertion-error@npm:^1.1.0": version: 1.1.0 resolution: "assertion-error@npm:1.1.0" @@ -1785,6 +1905,20 @@ __metadata: languageName: node linkType: hard +"astral-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "astral-regex@npm:2.0.0" + checksum: 10c0/f63d439cc383db1b9c5c6080d1e240bd14dae745f15d11ec5da863e182bbeca70df6c8191cffef5deba0b566ef98834610a68be79ac6379c95eeb26e1b310e25 + languageName: node + linkType: hard + +"async@npm:^3.2.0": + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 10c0/1408287b26c6db67d45cb346e34892cee555b8b59e6c68e6f8c3e495cad5ca13b4f218180e871f3c2ca30df4ab52693b66f2f6ff43644760cab0b2198bda79c1 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -1792,6 +1926,13 @@ __metadata: languageName: node linkType: hard +"at-least-node@npm:^1.0.0": + version: 1.0.0 + resolution: "at-least-node@npm:1.0.0" + checksum: 10c0/4c058baf6df1bc5a1697cf182e2029c58cd99975288a13f9e70068ef5d6f4e1f1fd7c4d2c3c4912eae44797d1725be9700995736deca441b39f3e66d8dee97ef + languageName: node + linkType: hard + "autoprefixer@npm:^10.4.19": version: 10.4.19 resolution: "autoprefixer@npm:10.4.19" @@ -1810,6 +1951,20 @@ __metadata: languageName: node linkType: hard +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: 10c0/021d2cc5547d4d9ef1633e0332e746a6f447997758b8b68d6fb33f290986872d2bff5f0c37d5832f41a7229361f093cd81c40898d96ed153493c0fb5cd8575d2 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.12.0 + resolution: "aws4@npm:1.12.0" + checksum: 10c0/1e39c266f53b04daf88e112de93a6006375b386a1b7ab6197260886e39abd012aa90bdd87949c3bf9c30754846031f6d5d8ac4f8676628097c11065b5d39847a + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -1817,6 +1972,22 @@ __metadata: languageName: node linkType: hard +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"bcrypt-pbkdf@npm:^1.0.0": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: "npm:^0.14.3" + checksum: 10c0/ddfe85230b32df25aeebfdccfbc61d3bc493ace49c884c9c68575de1f5dcf733a5d7de9def3b0f318b786616b8d85bad50a28b1da1750c43e0012c93badcc148 + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -1824,6 +1995,20 @@ __metadata: languageName: node linkType: hard +"blob-util@npm:^2.0.2": + version: 2.0.2 + resolution: "blob-util@npm:2.0.2" + checksum: 10c0/ed82d587827e5c86be122301a7c250f8364963e9582f72a826255bfbd32f8d69cc10169413d666667bb1c4fc8061329ae89d176ffe46fee8f32080af944ccddc + languageName: node + linkType: hard + +"bluebird@npm:^3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 10c0/680de03adc54ff925eaa6c7bb9a47a0690e8b5de60f4792604aae8ed618c65e6b63a7893b57ca924beaf53eee69c5af4f8314148c08124c550fe1df1add897d2 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -1866,6 +2051,23 @@ __metadata: languageName: node linkType: hard +"buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 10c0/cb0a8ddf5cf4f766466db63279e47761eb825693eeba6a5a95ee4ec8cb8f81ede70aa7f9d8aeec083e781d47154290eb5d4d26b3f7a465ec57fb9e7d59c47150 + languageName: node + linkType: hard + +"buffer@npm:^5.7.1": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -1893,6 +2095,26 @@ __metadata: languageName: node linkType: hard +"cachedir@npm:^2.3.0": + version: 2.4.0 + resolution: "cachedir@npm:2.4.0" + checksum: 10c0/76bff9009f2c446cd3777a4aede99af634a89670a67012b8041f65e951d3d36cefe8940341ea80c72219ee9913fa1f6146824cd9dfe9874a4bded728af7e6d76 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10c0/a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -1914,6 +2136,13 @@ __metadata: languageName: node linkType: hard +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: 10c0/ccf64bcb6c0232cdc5b7bd91ddd06e23a4b541f138336d4725233ac538041fb2f29c2e86c3c4a7a61ef990b665348db23a047060b9414c3a6603e9fa61ad4626 + languageName: node + linkType: hard + "chai@npm:^4.3.10": version: 4.4.1 resolution: "chai@npm:4.4.1" @@ -1976,6 +2205,13 @@ __metadata: languageName: node linkType: hard +"check-more-types@npm:^2.24.0": + version: 2.24.0 + resolution: "check-more-types@npm:2.24.0" + checksum: 10c0/93fda2c32eb5f6cd1161a84a2f4107c0e00b40a851748516791dd9a0992b91bdf504e3bf6bf7673ce603ae620042e11ed4084d16d6d92b36818abc9c2e725520 + languageName: node + linkType: hard + "chokidar@npm:^3.5.3": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -2002,6 +2238,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^3.2.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 10c0/6f0109e36e111684291d46123d491bc4e7b7a1934c3a20dea28cba89f1d4a03acd892f5f6a81ed3855c38647e285a150e3c9ba062e38943bef57fee6c1554c3a + languageName: node + linkType: hard + "class-variance-authority@npm:^0.7.0": version: 0.7.0 resolution: "class-variance-authority@npm:0.7.0" @@ -2018,6 +2261,15 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "cli-cursor@npm:3.1.0" + dependencies: + restore-cursor: "npm:^3.1.0" + checksum: 10c0/92a2f98ff9037d09be3dfe1f0d749664797fb674bf388375a2207a1203b69d41847abf16434203e0089212479e47a358b13a0222ab9fccfe8e2644a7ccebd111 + languageName: node + linkType: hard + "cli-cursor@npm:^4.0.0": version: 4.0.0 resolution: "cli-cursor@npm:4.0.0" @@ -2027,6 +2279,29 @@ __metadata: languageName: node linkType: hard +"cli-table3@npm:~0.6.1": + version: 0.6.4 + resolution: "cli-table3@npm:0.6.4" + dependencies: + "@colors/colors": "npm:1.5.0" + string-width: "npm:^4.2.0" + dependenciesMeta: + "@colors/colors": + optional: true + checksum: 10c0/8233c3d588db19122ed62a64256c7f0208232d2cece89a6cd7732481887fd9dcef69d976c4719149e77ccbf0a68f637bd5923536adccf6cdea051eeffa0ef1c2 + languageName: node + linkType: hard + +"cli-truncate@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-truncate@npm:2.1.0" + dependencies: + slice-ansi: "npm:^3.0.0" + string-width: "npm:^4.2.0" + checksum: 10c0/dfaa3df675bcef7a3254773de768712b590250420345a4c7ac151f041a4bacb4c25864b1377bee54a39b5925a030c00eabf014e312e3a4ac130952ed3b3879e9 + languageName: node + linkType: hard + "cli-truncate@npm:^4.0.0": version: 4.0.0 resolution: "cli-truncate@npm:4.0.0" @@ -2083,14 +2358,14 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.20": +"colorette@npm:^2.0.16, colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 languageName: node linkType: hard -"combined-stream@npm:^1.0.8": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -2113,6 +2388,20 @@ __metadata: languageName: node linkType: hard +"commander@npm:^6.2.1": + version: 6.2.1 + resolution: "commander@npm:6.2.1" + checksum: 10c0/85748abd9d18c8bc88febed58b98f66b7c591d9b5017cad459565761d7b29ca13b7783ea2ee5ce84bf235897333706c4ce29adf1ce15c8252780e7000e2ce9ea + languageName: node + linkType: hard + +"common-tags@npm:^1.8.0": + version: 1.8.2 + resolution: "common-tags@npm:1.8.2" + checksum: 10c0/23efe47ff0a1a7c91489271b3a1e1d2a171c12ec7f9b35b29b2fce51270124aff0ec890087e2bc2182c1cb746e232ab7561aaafe05f1e7452aea733d2bfe3f63 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -2127,6 +2416,13 @@ __metadata: languageName: node linkType: hard +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 10c0/980a37a93956d0de8a828ce508f9b9e3317039d68922ca79995421944146700e4aaf490a6dbfebcb1c5292a7184600c7710b957d724be1e37b8254c6bc0fe246 + languageName: node + linkType: hard + "country-emoji@npm:^1.5.6": version: 1.5.6 resolution: "country-emoji@npm:1.5.6" @@ -2177,6 +2473,67 @@ __metadata: languageName: node linkType: hard +"cypress@npm:^13.8.1": + version: 13.8.1 + resolution: "cypress@npm:13.8.1" + dependencies: + "@cypress/request": "npm:^3.0.0" + "@cypress/xvfb": "npm:^1.2.4" + "@types/sinonjs__fake-timers": "npm:8.1.1" + "@types/sizzle": "npm:^2.3.2" + arch: "npm:^2.2.0" + blob-util: "npm:^2.0.2" + bluebird: "npm:^3.7.2" + buffer: "npm:^5.7.1" + cachedir: "npm:^2.3.0" + chalk: "npm:^4.1.0" + check-more-types: "npm:^2.24.0" + cli-cursor: "npm:^3.1.0" + cli-table3: "npm:~0.6.1" + commander: "npm:^6.2.1" + common-tags: "npm:^1.8.0" + dayjs: "npm:^1.10.4" + debug: "npm:^4.3.4" + enquirer: "npm:^2.3.6" + eventemitter2: "npm:6.4.7" + execa: "npm:4.1.0" + executable: "npm:^4.1.1" + extract-zip: "npm:2.0.1" + figures: "npm:^3.2.0" + fs-extra: "npm:^9.1.0" + getos: "npm:^3.2.1" + is-ci: "npm:^3.0.1" + is-installed-globally: "npm:~0.4.0" + lazy-ass: "npm:^1.6.0" + listr2: "npm:^3.8.3" + lodash: "npm:^4.17.21" + log-symbols: "npm:^4.0.0" + minimist: "npm:^1.2.8" + ospath: "npm:^1.2.2" + pretty-bytes: "npm:^5.6.0" + process: "npm:^0.11.10" + proxy-from-env: "npm:1.0.0" + request-progress: "npm:^3.0.0" + semver: "npm:^7.5.3" + supports-color: "npm:^8.1.1" + tmp: "npm:~0.2.1" + untildify: "npm:^4.0.0" + yauzl: "npm:^2.10.0" + bin: + cypress: bin/cypress + checksum: 10c0/68e2604e628a54a714d071de1abcc0dc5392d15ac7159fe91c2e443866dcf22d457db4c07e78dc2e2617531d7732af56af41033e2cffee26d814e09a84214cd2 + languageName: node + linkType: hard + +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: "npm:^1.0.0" + checksum: 10c0/64589a15c5bd01fa41ff7007e0f2c6552c5ef2028075daa16b188a3721f4ba001841bf306dfc2eee6e2e6e7f76b38f5f17fb21fa847504192290ffa9e150118a + languageName: node + linkType: hard + "data-urls@npm:^5.0.0": version: 5.0.0 resolution: "data-urls@npm:5.0.0" @@ -2187,7 +2544,14 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": +"dayjs@npm:^1.10.4": + version: 1.11.11 + resolution: "dayjs@npm:1.11.11" + checksum: 10c0/0131d10516b9945f05a57e13f4af49a6814de5573a494824e103131a3bbe4cc470b1aefe8e17e51f9a478a22cd116084be1ee5725cedb66ec4c3f9091202dc4b + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -2199,6 +2563,15 @@ __metadata: languageName: node linkType: hard +"debug@npm:^3.1.0": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: "npm:^2.1.1" + checksum: 10c0/37d96ae42cbc71c14844d2ae3ba55adf462ec89fd3a999459dec3833944cd999af6007ff29c780f1c61153bcaaf2c842d1e4ce1ec621e4fc4923244942e4a02a + languageName: node + linkType: hard + "decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -2222,6 +2595,17 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -2303,10 +2687,20 @@ __metadata: languageName: node linkType: hard +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: "npm:~0.1.0" + safer-buffer: "npm:^2.1.0" + checksum: 10c0/6cf168bae1e2dad2e46561d9af9cbabfbf5ff592176ad4e9f0f41eaaf5fe5e10bb58147fe0a804de62b1ee9dad42c28810c88d652b21b6013c47ba8efa274ca1 + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.668": - version: 1.4.750 - resolution: "electron-to-chromium@npm:1.4.750" - checksum: 10c0/ce55df1b2a21a139ae998c39c3f1cf3f0ed1bb04078fcd415beec9daec695d6dbb21da0f54c539f9b11f430c1f4ee7062d331bffaa39cc7a492b7eb4f8bc4265 + version: 1.4.752 + resolution: "electron-to-chromium@npm:1.4.752" + checksum: 10c0/c3d577dc1d76ddf97a593f7c7c80b5be68d306064c1f81a791ce2803da6fef24e27a152e53066f36818160c04aa8018308ab12ebf71f67400ae67c2b3d044645 languageName: node linkType: hard @@ -2340,7 +2734,26 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.4.0": +"end-of-stream@npm:^1.1.0": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 + languageName: node + linkType: hard + +"enquirer@npm:^2.3.6": + version: 2.4.1 + resolution: "enquirer@npm:2.4.1" + dependencies: + ansi-colors: "npm:^4.1.1" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/43850479d7a51d36a9c924b518dcdc6373b5a8ae3401097d336b7b7e258324749d0ad37a1fcaa5706f04799baa05585cd7af19ebdf7667673e7694435fcea918 + languageName: node + linkType: hard + +"entities@npm:^4.4.0, entities@npm:^4.5.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 @@ -2361,6 +2774,22 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + "esbuild@npm:^0.20.1": version: 0.20.2 resolution: "esbuild@npm:0.20.2" @@ -2608,6 +3037,13 @@ __metadata: languageName: node linkType: hard +"eventemitter2@npm:6.4.7": + version: 6.4.7 + resolution: "eventemitter2@npm:6.4.7" + checksum: 10c0/35d8e9d51b919114eb072d33786274e1475db50efe00960c24c088ce4f76c07a826ccc927602724928efb3d8f09a7d8dd1fa79e410875118c0e9846959287f34 + languageName: node + linkType: hard + "eventemitter3@npm:^5.0.1": version: 5.0.1 resolution: "eventemitter3@npm:5.0.1" @@ -2615,6 +3051,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:4.1.0": + version: 4.1.0 + resolution: "execa@npm:4.1.0" + dependencies: + cross-spawn: "npm:^7.0.0" + get-stream: "npm:^5.0.0" + human-signals: "npm:^1.1.1" + is-stream: "npm:^2.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^4.0.0" + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + strip-final-newline: "npm:^2.0.0" + checksum: 10c0/02211601bb1c52710260edcc68fb84c3c030dc68bafc697c90ada3c52cc31375337de8c24826015b8382a58d63569ffd203b79c94fef217d65503e3e8d2c52ba + languageName: node + linkType: hard + "execa@npm:8.0.1, execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" @@ -2632,6 +3085,15 @@ __metadata: languageName: node linkType: hard +"executable@npm:^4.1.1": + version: 4.1.1 + resolution: "executable@npm:4.1.1" + dependencies: + pify: "npm:^2.2.0" + checksum: 10c0/c3cc5d2d2e3cdb1b7d7b0639ebd5566d113d7ada21cfa07f5226d55ba2a210320116720e07570ed5659ef2ec516bc00c8f0488dac75d112fd324ef25c2100173 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -2639,6 +3101,44 @@ __metadata: languageName: node linkType: hard +"extend@npm:~3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 + languageName: node + linkType: hard + +"extract-zip@npm:2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": "npm:^2.9.1" + debug: "npm:^4.1.1" + get-stream: "npm:^5.1.0" + yauzl: "npm:^2.10.0" + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 10c0/9afbd46854aa15a857ae0341a63a92743a7b89c8779102c3b4ffc207516b2019337353962309f85c66ee3d9092202a83cdc26dbf449a11981272038443974aee + languageName: node + linkType: hard + +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: 10c0/f75114a8388f0cbce68e277b6495dc3930db4dde1611072e4a140c24e204affd77320d004b947a132e9a3b97b8253017b2b62dce661975fb0adced707abf1ab5 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: 10c0/e10e2769985d0e9b6c7199b053a9957589d02e84de42832c295798cb422a025e6d4a92e0259c1fb4d07090f5bfde6b55fd9f880ac5855bd61d775f8ab75a7ab0 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -2646,7 +3146,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -2682,6 +3182,31 @@ __metadata: languageName: node linkType: hard +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: "npm:~1.2.0" + checksum: 10c0/304dd70270298e3ffe3bcc05e6f7ade2511acc278bc52d025f8918b48b6aa3b77f10361bddfadfe2a28163f7af7adbdce96f4d22c31b2f648ba2901f0c5fc20e + languageName: node + linkType: hard + +"fflate@npm:^0.8.1": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293 + languageName: node + linkType: hard + +"figures@npm:^3.2.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/9c421646ede432829a50bc4e55c7a4eb4bcb7cc07b5bab2f471ef1ab9a344595bbebb6c5c21470093fbb730cd81bbca119624c40473a125293f656f49cb47629 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -2738,6 +3263,13 @@ __metadata: languageName: node linkType: hard +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 10c0/364f7f5f7d93ab661455351ce116a67877b66f59aca199559a999bd39e3cfadbfbfacc10415a915255e2210b30c23febe9aec3ca16bf2d1ff11c935a1000e24c + languageName: node + linkType: hard + "form-data@npm:^4.0.0": version: 4.0.0 resolution: "form-data@npm:4.0.0" @@ -2749,7 +3281,18 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.7": +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.6" + mime-types: "npm:^2.1.12" + checksum: 10c0/706ef1e5649286b6a61e5bb87993a9842807fd8f149cd2548ee807ea4fb882247bdf7f6e64ac4720029c0cd5c80343de0e22eee1dc9e9882e12db9cc7bc016a4 + languageName: node + linkType: hard + +"fraction.js@npm:^4.3.7": version: 4.3.7 resolution: "fraction.js@npm:4.3.7" checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 @@ -2774,14 +3317,17 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^7.2.0" "@typescript-eslint/parser": "npm:^7.2.0" "@vitejs/plugin-react-swc": "npm:^3.5.0" + "@vitest/ui": "npm:^1.5.3" autoprefixer: "npm:^10.4.19" class-variance-authority: "npm:^0.7.0" clsx: "npm:^2.1.1" country-emoji: "npm:^1.5.6" + cypress: "npm:^13.8.1" eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-react-hooks: "npm:^4.6.0" eslint-plugin-react-refresh: "npm:^0.4.6" + happy-dom: "npm:^14.7.1" husky: "npm:^9.0.11" jsdom: "npm:^24.0.0" lint-staged: "npm:^15.2.2" @@ -2803,6 +3349,18 @@ __metadata: languageName: unknown linkType: soft +"fs-extra@npm:^9.1.0": + version: 9.1.0 + resolution: "fs-extra@npm:9.1.0" + dependencies: + at-least-node: "npm:^1.0.0" + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/9b808bd884beff5cb940773018179a6b94a966381d005479f00adda6b44e5e3d4abf765135773d849cc27efe68c349e4a7b86acd7d3306d5932c14f3a4b17a92 + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -2868,6 +3426,19 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + has-proto: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.0" + checksum: 10c0/0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 + languageName: node + linkType: hard + "get-nonce@npm:^1.0.0": version: 1.0.1 resolution: "get-nonce@npm:1.0.1" @@ -2875,6 +3446,15 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: "npm:^3.0.0" + checksum: 10c0/43797ffd815fbb26685bf188c8cfebecb8af87b3925091dd7b9a9c915993293d78e3c9e1bce125928ff92f2d0796f3889b92b5ec6d58d1041b574682132e0a80 + languageName: node + linkType: hard + "get-stream@npm:^8.0.1": version: 8.0.1 resolution: "get-stream@npm:8.0.1" @@ -2882,6 +3462,24 @@ __metadata: languageName: node linkType: hard +"getos@npm:^3.2.1": + version: 3.2.1 + resolution: "getos@npm:3.2.1" + dependencies: + async: "npm:^3.2.0" + checksum: 10c0/21556fca1da4dfc8f1707261b4f9ff19b9e9bfefa76478249d2abddba3cd014bd6c5360634add1590b27e0b27d422e8f997dddaa0234aae1fa4c54f33f82e841 + languageName: node + linkType: hard + +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: "npm:^1.0.0" + checksum: 10c0/c13f8530ecf16fc509f3fa5cd8dd2129ffa5d0c7ccdf5728b6022d52954c2d24be3706b4cdf15333eec52f1fbb43feb70a01dabc639d1d10071e371da8aaa52f + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -2929,6 +3527,15 @@ __metadata: languageName: node linkType: hard +"global-dirs@npm:^3.0.0": + version: 3.0.1 + resolution: "global-dirs@npm:3.0.1" + dependencies: + ini: "npm:2.0.0" + checksum: 10c0/ef65e2241a47ff978f7006a641302bc7f4c03dfb98783d42bf7224c136e3a06df046e70ee3a010cf30214114755e46c9eb5eb1513838812fbbe0d92b14c25080 + languageName: node + linkType: hard + "globals@npm:^13.19.0": version: 13.24.0 resolution: "globals@npm:13.24.0" @@ -2952,7 +3559,16 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.6": +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10c0/505c05487f7944c552cee72087bf1567debb470d4355b1335f2c262d218ebbff805cd3715448fe29b4b380bae6912561d0467233e4165830efd28da241418c63 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -2966,6 +3582,17 @@ __metadata: languageName: node linkType: hard +"happy-dom@npm:^14.7.1": + version: 14.7.1 + resolution: "happy-dom@npm:14.7.1" + dependencies: + entities: "npm:^4.5.0" + webidl-conversions: "npm:^7.0.0" + whatwg-mimetype: "npm:^3.0.0" + checksum: 10c0/546a16921cde2cad91a35eefc936eb26df444d95e976e01276052e5fd2db3a8dd5864182475ebbfc8443689ce306246d8e6dfec40bab5fb8bc99e2a448b795e9 + languageName: node + linkType: hard + "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -2980,6 +3607,29 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10c0/35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: 10c0/e6922b4345a3f37069cdfe8600febbca791c94988c01af3394d86ca3360b4b93928bbf395859158f88099cb10b19d98e3bbab7c9ff2c1bd09cf665ee90afa2c3 + languageName: node + linkType: hard + "hasown@npm:^2.0.0": version: 2.0.2 resolution: "hasown@npm:2.0.2" @@ -3015,6 +3665,17 @@ __metadata: languageName: node linkType: hard +"http-signature@npm:~1.3.6": + version: 1.3.6 + resolution: "http-signature@npm:1.3.6" + dependencies: + assert-plus: "npm:^1.0.0" + jsprim: "npm:^2.0.2" + sshpk: "npm:^1.14.1" + checksum: 10c0/f8d15d8c91a5a80805530e2f401a3f83ed55162058651d86ad00df294b159a54e001b5d00e04983f7542a55865aee02d2d83d68c8499137ff2bc142553d8dfc2 + languageName: node + linkType: hard + "https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.4 resolution: "https-proxy-agent@npm:7.0.4" @@ -3025,6 +3686,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^1.1.1": + version: 1.1.1 + resolution: "human-signals@npm:1.1.1" + checksum: 10c0/18810ed239a7a5e23fb6c32d0fd4be75d7cd337a07ad59b8dbf0794cb0761e6e628349ee04c409e605fe55344716eab5d0a47a62ba2a2d0d367c89a2b4247b1e + languageName: node + linkType: hard + "human-signals@npm:^5.0.0": version: 5.0.0 resolution: "human-signals@npm:5.0.0" @@ -3050,6 +3718,13 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.1.13": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + "ignore@npm:^5.2.0, ignore@npm:^5.3.1": version: 5.3.1 resolution: "ignore@npm:5.3.1" @@ -3098,6 +3773,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:2.0.0": + version: 2.0.0 + resolution: "ini@npm:2.0.0" + checksum: 10c0/2e0c8f386369139029da87819438b20a1ff3fe58372d93fb1a86e9d9344125ace3a806b8ec4eb160a46e64cbc422fe68251869441676af49b7fc441af2389c25 + languageName: node + linkType: hard + "invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -3126,6 +3808,17 @@ __metadata: languageName: node linkType: hard +"is-ci@npm:^3.0.1": + version: 3.0.1 + resolution: "is-ci@npm:3.0.1" + dependencies: + ci-info: "npm:^3.2.0" + bin: + is-ci: bin.js + checksum: 10c0/0e81caa62f4520d4088a5bef6d6337d773828a88610346c4b1119fb50c842587ed8bef1e5d9a656835a599e7209405b5761ddf2339668f2d0f4e889a92fe6051 + languageName: node + linkType: hard + "is-core-module@npm:^2.13.0": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" @@ -3174,6 +3867,16 @@ __metadata: languageName: node linkType: hard +"is-installed-globally@npm:~0.4.0": + version: 0.4.0 + resolution: "is-installed-globally@npm:0.4.0" + dependencies: + global-dirs: "npm:^3.0.0" + is-path-inside: "npm:^3.0.2" + checksum: 10c0/f3e6220ee5824b845c9ed0d4b42c24272701f1f9926936e30c0e676254ca5b34d1b92c6205cae11b283776f9529212c0cdabb20ec280a6451677d6493ca9c22d + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -3188,7 +3891,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.3": +"is-path-inside@npm:^3.0.2, is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 @@ -3202,6 +3905,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + "is-stream@npm:^3.0.0": version: 3.0.0 resolution: "is-stream@npm:3.0.0" @@ -3209,6 +3919,20 @@ __metadata: languageName: node linkType: hard +"is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 10c0/4c096275ba041a17a13cca33ac21c16bc4fd2d7d7eb94525e7cd2c2f2c1a3ab956e37622290642501ff4310601e413b675cf399ad6db49855527d2163b3eeeec + languageName: node + linkType: hard + +"is-unicode-supported@npm:^0.1.0": + version: 0.1.0 + resolution: "is-unicode-supported@npm:0.1.0" + checksum: 10c0/00cbe3455c3756be68d2542c416cab888aebd5012781d6819749fefb15162ff23e38501fe681b3d751c73e8ff561ac09a5293eba6f58fdf0178462ce6dcb3453 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -3223,6 +3947,13 @@ __metadata: languageName: node linkType: hard +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 10c0/a6686a878735ca0a48e0d674dd6d8ad31aedfaf70f07920da16ceadc7577b46d67179a60b313f2e6860cb097a2c2eb3cbd0b89e921ae89199a59a17c3273d66f + languageName: node + linkType: hard + "jackspeak@npm:^2.3.6": version: 2.3.6 resolution: "jackspeak@npm:2.3.6" @@ -3277,6 +4008,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: 10c0/e046e05c59ff880ee4ef68902dbdcb6d2f3c5d60c357d4d68647dc23add556c31c0e5f41bdb7e69e793dd63468bd9e085da3636341048ef577b18f5b713877c0 + languageName: node + linkType: hard + "jsdom@npm:^24.0.0": version: 24.0.0 resolution: "jsdom@npm:24.0.0" @@ -3325,6 +4063,13 @@ __metadata: languageName: node linkType: hard +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 10c0/d4a637ec1d83544857c1c163232f3da46912e971d5bf054ba44fdb88f07d8d359a462b4aec46f2745efbc57053365608d88bc1d7b1729f7b4fc3369765639ed3 + languageName: node + linkType: hard + "json-stable-stringify-without-jsonify@npm:^1.0.1": version: 1.0.1 resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" @@ -3332,6 +4077,38 @@ __metadata: languageName: node linkType: hard +"json-stringify-safe@npm:~5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 10c0/7dbf35cd0411d1d648dceb6d59ce5857ec939e52e4afc37601aa3da611f0987d5cee5b38d58329ceddf3ed48bd7215229c8d52059ab01f2444a338bf24ed0f37 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + +"jsprim@npm:^2.0.2": + version: 2.0.2 + resolution: "jsprim@npm:2.0.2" + dependencies: + assert-plus: "npm:1.0.0" + extsprintf: "npm:1.3.0" + json-schema: "npm:0.4.0" + verror: "npm:1.10.0" + checksum: 10c0/677be2d41df536c92c6d0114a492ef197084018cfbb1a3e10b1fa1aad889564b2e3a7baa6af7949cc2d73678f42368b0be165a26bd4e4de6883a30dd6a24e98d + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -3341,6 +4118,13 @@ __metadata: languageName: node linkType: hard +"lazy-ass@npm:^1.6.0": + version: 1.6.0 + resolution: "lazy-ass@npm:1.6.0" + checksum: 10c0/4af6cb9a333fbc811268c745f9173fba0f99ecb817cc9c0fae5dbf986b797b730ff525504128f6623b91aba32b02124553a34b0d14de3762b637b74d7233f3bd + languageName: node + linkType: hard + "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -3413,6 +4197,27 @@ __metadata: languageName: node linkType: hard +"listr2@npm:^3.8.3": + version: 3.14.0 + resolution: "listr2@npm:3.14.0" + dependencies: + cli-truncate: "npm:^2.1.0" + colorette: "npm:^2.0.16" + log-update: "npm:^4.0.0" + p-map: "npm:^4.0.0" + rfdc: "npm:^1.3.0" + rxjs: "npm:^7.5.1" + through: "npm:^2.3.8" + wrap-ansi: "npm:^7.0.0" + peerDependencies: + enquirer: ">= 2.3.0 < 3" + peerDependenciesMeta: + enquirer: + optional: true + checksum: 10c0/8301703876ad6bf50cd769e9c1169c2aa435951d69d4f54fc202a13c1b6006a9b3afbcf9842440eb22f08beec4d311d365e31d4ed2e0fcabf198d8085b06a421 + languageName: node + linkType: hard + "local-pkg@npm:^0.5.0": version: 0.5.0 resolution: "local-pkg@npm:0.5.0" @@ -3439,13 +4244,42 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15": +"lodash.once@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: 10c0/46a9a0a66c45dd812fcc016e46605d85ad599fe87d71a02f6736220554b52ffbe82e79a483ad40f52a8a95755b0d1077fba259da8bfb6694a7abbf4a48f1fc04 + languageName: node + linkType: hard + +"lodash@npm:^4.17.15, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c languageName: node linkType: hard +"log-symbols@npm:^4.0.0": + version: 4.1.0 + resolution: "log-symbols@npm:4.1.0" + dependencies: + chalk: "npm:^4.1.0" + is-unicode-supported: "npm:^0.1.0" + checksum: 10c0/67f445a9ffa76db1989d0fa98586e5bc2fd5247260dafb8ad93d9f0ccd5896d53fb830b0e54dade5ad838b9de2006c826831a3c528913093af20dff8bd24aca6 + languageName: node + linkType: hard + +"log-update@npm:^4.0.0": + version: 4.0.0 + resolution: "log-update@npm:4.0.0" + dependencies: + ansi-escapes: "npm:^4.3.0" + cli-cursor: "npm:^3.1.0" + slice-ansi: "npm:^4.0.0" + wrap-ansi: "npm:^6.2.0" + checksum: 10c0/18b299e230432a156f2535660776406d15ba8bb7817dd3eaadd58004b363756d4ecaabcd658f9949f90b62ea7d3354423be3fdeb7a201ab951ec0e8d6139af86 + languageName: node + linkType: hard + "log-update@npm:^6.0.0": version: 6.0.0 resolution: "log-update@npm:6.0.0" @@ -3523,8 +4357,8 @@ __metadata: linkType: hard "make-fetch-happen@npm:^13.0.0": - version: 13.0.0 - resolution: "make-fetch-happen@npm:13.0.0" + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" dependencies: "@npmcli/agent": "npm:^2.0.0" cacache: "npm:^18.0.0" @@ -3535,9 +4369,10 @@ __metadata: minipass-flush: "npm:^1.0.5" minipass-pipeline: "npm:^1.2.4" negotiator: "npm:^0.6.3" + proc-log: "npm:^4.2.0" promise-retry: "npm:^2.0.1" ssri: "npm:^10.0.0" - checksum: 10c0/43b9f6dcbc6fe8b8604cb6396957c3698857a15ba4dbc38284f7f0e61f248300585ef1eb8cc62df54e9c724af977e45b5cdfd88320ef7f53e45070ed3488da55 + checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e languageName: node linkType: hard @@ -3572,7 +4407,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12": +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -3620,6 +4455,13 @@ __metadata: languageName: node linkType: hard +"minimist@npm:^1.2.8": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + "minipass-collect@npm:^2.0.1": version: 2.0.1 resolution: "minipass-collect@npm:2.0.1" @@ -3725,6 +4567,13 @@ __metadata: languageName: node linkType: hard +"mrmime@npm:^2.0.0": + version: 2.0.0 + resolution: "mrmime@npm:2.0.0" + checksum: 10c0/312b35ed288986aec90955410b21ed7427fd1e4ee318cb5fc18765c8d029eeded9444faa46589e5b1ed6b35fb2054a802ac8dcb917ddf6b3e189cb3bf11a965c + languageName: node + linkType: hard + "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -3732,6 +4581,13 @@ __metadata: languageName: node linkType: hard +"ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -3818,6 +4674,15 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^4.0.0": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: "npm:^3.0.0" + checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac + languageName: node + linkType: hard + "npm-run-path@npm:^5.1.0": version: 5.3.0 resolution: "npm-run-path@npm:5.3.0" @@ -3848,7 +4713,14 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": +"object-inspect@npm:^1.13.1": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 10c0/fad603f408e345c82e946abdf4bfd774260a5ed3e5997a0b057c44153ac32c7271ff19e3a5ae39c858da683ba045ccac2f65245c12763ce4e8594f818f4a648d + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -3889,6 +4761,13 @@ __metadata: languageName: node linkType: hard +"ospath@npm:^1.2.2": + version: 1.2.2 + resolution: "ospath@npm:1.2.2" + checksum: 10c0/e485a6ca91964f786163408b093860bf26a9d9704d83ec39ccf463b9f11ea712b780b23b73d1f64536de62c5f66244dd94ed83fc9ffe3c1564dd1eed5cdae923 + languageName: node + linkType: hard + "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -3957,7 +4836,7 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^3.1.0": +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c @@ -4009,6 +4888,20 @@ __metadata: languageName: node linkType: hard +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 10c0/8a87e63f7a4afcfb0f9f77b39bb92374afc723418b9cb716ee4257689224171002e07768eeade4ecd0e86f1fa3d8f022994219fb45634f2dbd78c6803e452458 + languageName: node + linkType: hard + +"performance-now@npm:^2.1.0": + version: 2.1.0 + resolution: "performance-now@npm:2.1.0" + checksum: 10c0/22c54de06f269e29f640e0e075207af57de5052a3d15e360c09b9a8663f393f6f45902006c1e71aa8a5a1cdfb1a47fe268826f8496d6425c362f00f5bc3e85d9 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -4032,7 +4925,7 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.3.0": +"pify@npm:^2.2.0, pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc @@ -4209,6 +5102,13 @@ __metadata: languageName: node linkType: hard +"pretty-bytes@npm:^5.6.0": + version: 5.6.0 + resolution: "pretty-bytes@npm:5.6.0" + checksum: 10c0/f69f494dcc1adda98dbe0e4a36d301e8be8ff99bfde7a637b2ee2820e7cb583b0fc0f3a63b0e3752c01501185a5cf38602c7be60da41bdf84ef5b70e89c370f3 + languageName: node + linkType: hard + "pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" @@ -4238,6 +5138,20 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9 + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -4248,6 +5162,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:1.0.0": + version: 1.0.0 + resolution: "proxy-from-env@npm:1.0.0" + checksum: 10c0/c64df9b21f7f820dc882cd6f7f81671840acd28b9688ee3e3e6af47a56ec7f0edcabe5bc96b32b26218b35eeff377bcc27ac27f89b6b21401003e187ff13256f + languageName: node + linkType: hard + "psl@npm:^1.1.33": version: 1.9.0 resolution: "psl@npm:1.9.0" @@ -4255,6 +5176,16 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + languageName: node + linkType: hard + "punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -4262,6 +5193,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.10.4": + version: 6.10.4 + resolution: "qs@npm:6.10.4" + dependencies: + side-channel: "npm:^1.0.4" + checksum: 10c0/7a8c9d77968aeccb769aedd7e047c0e0109dad0cfa57cab1ad906f4069fd58f361b80abd2de5854ba9a09b4c5d06d6a2c82108766f1f1527572fe6130deaa471 + languageName: node + linkType: hard + "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -4422,6 +5362,15 @@ __metadata: languageName: node linkType: hard +"request-progress@npm:^3.0.0": + version: 3.0.0 + resolution: "request-progress@npm:3.0.0" + dependencies: + throttleit: "npm:^1.0.0" + checksum: 10c0/d5dcb7155a738572c8781436f6b418e866066a30eea0f99a9ab26b6f0ed6c13637462bba736357de3899b8d30431ee9202ac956a5f8ccdd0d9d1ed0962000d14 + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -4462,6 +5411,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "restore-cursor@npm:3.1.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/8051a371d6aa67ff21625fa94e2357bd81ffdc96267f3fb0fc4aaf4534028343836548ef34c240ffa8c25b280ca35eb36be00b3cb2133fa4f51896d7e73c6b4f + languageName: node + linkType: hard + "restore-cursor@npm:^4.0.0": version: 4.0.0 resolution: "restore-cursor@npm:4.0.0" @@ -4505,25 +5464,25 @@ __metadata: linkType: hard "rollup@npm:^4.13.0": - version: 4.17.1 - resolution: "rollup@npm:4.17.1" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.17.1" - "@rollup/rollup-android-arm64": "npm:4.17.1" - "@rollup/rollup-darwin-arm64": "npm:4.17.1" - "@rollup/rollup-darwin-x64": "npm:4.17.1" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.17.1" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.17.1" - "@rollup/rollup-linux-arm64-gnu": "npm:4.17.1" - "@rollup/rollup-linux-arm64-musl": "npm:4.17.1" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.17.1" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.17.1" - "@rollup/rollup-linux-s390x-gnu": "npm:4.17.1" - "@rollup/rollup-linux-x64-gnu": "npm:4.17.1" - "@rollup/rollup-linux-x64-musl": "npm:4.17.1" - "@rollup/rollup-win32-arm64-msvc": "npm:4.17.1" - "@rollup/rollup-win32-ia32-msvc": "npm:4.17.1" - "@rollup/rollup-win32-x64-msvc": "npm:4.17.1" + version: 4.17.2 + resolution: "rollup@npm:4.17.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.17.2" + "@rollup/rollup-android-arm64": "npm:4.17.2" + "@rollup/rollup-darwin-arm64": "npm:4.17.2" + "@rollup/rollup-darwin-x64": "npm:4.17.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.17.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.17.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.17.2" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.17.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.17.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-x64-musl": "npm:4.17.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.17.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.17.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.17.2" "@types/estree": "npm:1.0.5" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -4563,7 +5522,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/43a5b890a40a0390721ee0ffb1bef08d78c2a251060ef74886d3f85985227d4b0f4fbabdca61ebbd9fd159d442bb0c0ca8ba7cb15a9602041b219291e8d1511e + checksum: 10c0/4fa6644e5c7fc4a34f654ea7e209be6c2c5897ed9dd43e7135230137204df748a795c7553804130f6c41da0b71e83f8c35a4a7881d385a77996adee50b609a6e languageName: node linkType: hard @@ -4583,7 +5542,23 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3.0.0": +"rxjs@npm:^7.5.1": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/3c49c1ecd66170b175c9cacf5cef67f8914dcbc7cd0162855538d365c83fea631167cacb644b3ce533b2ea0e9a4d0b12175186985f89d75abe73dbd8f7f06f68 + languageName: node + linkType: hard + +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.2": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 @@ -4608,7 +5583,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.6.0": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -4619,6 +5594,20 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -4635,6 +5624,18 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.0.4": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10c0/d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f + languageName: node + linkType: hard + "siginfo@npm:^2.0.0": version: 2.0.0 resolution: "siginfo@npm:2.0.0" @@ -4656,6 +5657,17 @@ __metadata: languageName: node linkType: hard +"sirv@npm:^2.0.4": + version: 2.0.4 + resolution: "sirv@npm:2.0.4" + dependencies: + "@polka/url": "npm:^1.0.0-next.24" + mrmime: "npm:^2.0.0" + totalist: "npm:^3.0.0" + checksum: 10c0/68f8ee857f6a9415e9c07a1f31c7c561df8d5f1b1ba79bee3de583fa37da8718def5309f6b1c6e2c3ef77de45d74f5e49efc7959214443aa92d42e9c99180a4e + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -4663,6 +5675,28 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "slice-ansi@npm:3.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + astral-regex: "npm:^2.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + checksum: 10c0/88083c9d0ca67d09f8b4c78f68833d69cabbb7236b74df5d741ad572bbf022deaf243fa54009cd434350622a1174ab267710fcc80a214ecc7689797fe00cb27c + languageName: node + linkType: hard + +"slice-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "slice-ansi@npm:4.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + astral-regex: "npm:^2.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + checksum: 10c0/6c25678db1270d4793e0327620f1e0f9f5bea4630123f51e9e399191bc52c87d6e6de53ed33538609e5eacbd1fab769fae00f3705d08d029f02102a540648918 + languageName: node + linkType: hard + "slice-ansi@npm:^5.0.0": version: 5.0.0 resolution: "slice-ansi@npm:5.0.0" @@ -4725,6 +5759,27 @@ __metadata: languageName: node linkType: hard +"sshpk@npm:^1.14.1": + version: 1.18.0 + resolution: "sshpk@npm:1.18.0" + dependencies: + asn1: "npm:~0.2.3" + assert-plus: "npm:^1.0.0" + bcrypt-pbkdf: "npm:^1.0.0" + dashdash: "npm:^1.12.0" + ecc-jsbn: "npm:~0.1.1" + getpass: "npm:^0.1.1" + jsbn: "npm:~0.1.0" + safer-buffer: "npm:^2.0.2" + tweetnacl: "npm:~0.14.0" + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: 10c0/e516e34fa981cfceef45fd2e947772cc70dbd57523e5c608e2cd73752ba7f8a99a04df7c3ed751588e8d91956b6f16531590b35d3489980d1c54c38bebcd41b1 + languageName: node + linkType: hard + "ssri@npm:^10.0.0": version: 10.0.5 resolution: "ssri@npm:10.0.5" @@ -4755,7 +5810,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -4806,6 +5861,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f + languageName: node + linkType: hard + "strip-final-newline@npm:^3.0.0": version: 3.0.0 resolution: "strip-final-newline@npm:3.0.0" @@ -4874,6 +5936,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -4978,6 +6049,20 @@ __metadata: languageName: node linkType: hard +"throttleit@npm:^1.0.0": + version: 1.0.1 + resolution: "throttleit@npm:1.0.1" + checksum: 10c0/4d41a1bf467646b1aa7bec0123b78452a0e302d7344f6a67e43e68434f0a02ea3ba44df050a40c69adeb9cae3cbf6b36b38cfe94bcc3c4a8243c9b63e38e059b + languageName: node + linkType: hard + +"through@npm:^2.3.8": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: 10c0/4b09f3774099de0d4df26d95c5821a62faee32c7e96fb1f4ebd54a2d7c11c57fe88b0a0d49cf375de5fee5ae6bf4eb56dbbf29d07366864e2ee805349970d3cc + languageName: node + linkType: hard + "tinybench@npm:^2.5.1": version: 2.8.0 resolution: "tinybench@npm:2.8.0" @@ -4999,6 +6084,13 @@ __metadata: languageName: node linkType: hard +"tmp@npm:~0.2.1": + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -5008,15 +6100,22 @@ __metadata: languageName: node linkType: hard +"totalist@npm:^3.0.0": + version: 3.0.1 + resolution: "totalist@npm:3.0.1" + checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863 + languageName: node + linkType: hard + "tough-cookie@npm:^4.1.3": - version: 4.1.3 - resolution: "tough-cookie@npm:4.1.3" + version: 4.1.4 + resolution: "tough-cookie@npm:4.1.4" dependencies: psl: "npm:^1.1.33" punycode: "npm:^2.1.1" universalify: "npm:^0.2.0" url-parse: "npm:^1.5.3" - checksum: 10c0/4fc0433a0cba370d57c4b240f30440c848906dee3180bb6e85033143c2726d322e7e4614abb51d42d111ebec119c4876ed8d7247d4113563033eebbc1739c831 + checksum: 10c0/aca7ff96054f367d53d1e813e62ceb7dd2eda25d7752058a74d64b7266fd07be75908f3753a32ccf866a2f997604b414cfb1916d6e7f69bc64d9d9939b0d6c45 languageName: node linkType: hard @@ -5052,6 +6151,22 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a + languageName: node + linkType: hard + +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 10c0/4612772653512c7bc19e61923fbf42903f5e0389ec76a4a1f17195859d114671ea4aa3b734c2029ce7e1fa7e5cc8b80580f67b071ecf0b46b5636d030a0102a2 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -5075,6 +6190,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 + languageName: node + linkType: hard + "typescript@npm:^5.2.2": version: 5.4.5 resolution: "typescript@npm:5.4.5" @@ -5134,6 +6256,20 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 10c0/d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -5214,9 +6350,29 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.5.2": - version: 1.5.2 - resolution: "vite-node@npm:1.5.2" +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 + languageName: node + linkType: hard + +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: "npm:^1.0.0" + core-util-is: "npm:1.0.2" + extsprintf: "npm:^1.2.0" + checksum: 10c0/37ccdf8542b5863c525128908ac80f2b476eed36a32cb944de930ca1e2e78584cc435c4b9b4c68d0fc13a47b45ff364b4be43aa74f8804f9050140f660fb660d + languageName: node + linkType: hard + +"vite-node@npm:1.5.3": + version: 1.5.3 + resolution: "vite-node@npm:1.5.3" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.4" @@ -5225,7 +6381,7 @@ __metadata: vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: 10c0/a5e90ae2b3ec912fbdf22ecf10f1b0d769e2fd1f24c5e4090488be03068b994dbcf9bea492d5c5fbde77f45a0e328e90a48a9ecdcb5eab30709e6d221aa0c7bd + checksum: 10c0/beddb9cc4377fc58726ea08206d20751b0cf1efd1b632b9048ff73184bf4c7b9e586490b48ddeb9c4be3ddea9dbfa9d32125f060a78c6d705be670bbe03ecc95 languageName: node linkType: hard @@ -5270,14 +6426,14 @@ __metadata: linkType: hard "vitest@npm:^1.5.2": - version: 1.5.2 - resolution: "vitest@npm:1.5.2" - dependencies: - "@vitest/expect": "npm:1.5.2" - "@vitest/runner": "npm:1.5.2" - "@vitest/snapshot": "npm:1.5.2" - "@vitest/spy": "npm:1.5.2" - "@vitest/utils": "npm:1.5.2" + version: 1.5.3 + resolution: "vitest@npm:1.5.3" + dependencies: + "@vitest/expect": "npm:1.5.3" + "@vitest/runner": "npm:1.5.3" + "@vitest/snapshot": "npm:1.5.3" + "@vitest/spy": "npm:1.5.3" + "@vitest/utils": "npm:1.5.3" acorn-walk: "npm:^8.3.2" chai: "npm:^4.3.10" debug: "npm:^4.3.4" @@ -5291,13 +6447,13 @@ __metadata: tinybench: "npm:^2.5.1" tinypool: "npm:^0.8.3" vite: "npm:^5.0.0" - vite-node: "npm:1.5.2" + vite-node: "npm:1.5.3" why-is-node-running: "npm:^2.2.2" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 1.5.2 - "@vitest/ui": 1.5.2 + "@vitest/browser": 1.5.3 + "@vitest/ui": 1.5.3 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -5315,7 +6471,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10c0/1ad3a33d3ab5faeb4baa9c9ba62b34e94c00a6e140ba2a8589224d6b9db89e3a3d6753d1d1ecb85cbcb0f9023b22d8066dfde7f31e1733484f2ea5cf640f4969 + checksum: 10c0/2bce3a46a17b6e6915bdbdf60d25724b996c95a9e2c43cea483a6b5af527cd75030ec5215723fee494ba09608bf6339793511863dc0af1b3aaeea9eb3fda24e3 languageName: node linkType: hard @@ -5344,6 +6500,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^3.0.0": + version: 3.0.0 + resolution: "whatwg-mimetype@npm:3.0.0" + checksum: 10c0/323895a1cda29a5fb0b9ca82831d2c316309fede0365047c4c323073e3239067a304a09a1f4b123b9532641ab604203f33a1403b5ca6a62ef405bcd7a204080f + languageName: node + linkType: hard + "whatwg-mimetype@npm:^4.0.0": version: 4.0.0 resolution: "whatwg-mimetype@npm:4.0.0" @@ -5402,7 +6565,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" dependencies: @@ -5413,6 +6576,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/baad244e6e33335ea24e86e51868fe6823626e3a3c88d9a6674642afff1d34d9a154c917e74af8d845fd25d170c4ea9cf69a47133c3f3656e1252b3d462d9f6c + languageName: node + linkType: hard + "wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" @@ -5494,6 +6668,16 @@ __metadata: languageName: node linkType: hard +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: "npm:~0.2.3" + fd-slicer: "npm:~1.1.0" + checksum: 10c0/f265002af7541b9ec3589a27f5fb8f11cf348b53cc15e2751272e3c062cd73f3e715bc72d43257de71bbaecae446c3f1b14af7559e8ab0261625375541816422 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 615cb03e6db0924c395dd32c4e9a4cb03008e7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Mon, 6 May 2024 14:46:40 +0300 Subject: [PATCH 09/74] chore(devops): auto-deploy backend staging --- .github/workflows/backend_deploy_staging.yml | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/backend_deploy_staging.yml diff --git a/.github/workflows/backend_deploy_staging.yml b/.github/workflows/backend_deploy_staging.yml new file mode 100644 index 00000000..fdcbb40b --- /dev/null +++ b/.github/workflows/backend_deploy_staging.yml @@ -0,0 +1,43 @@ +name: backend-staging-workflow + +on: + push: + branches: + - staging + paths: + - 'backend/**' + - docker-compose.yml + +jobs: + test: + runs-on: ubuntu-latest + env: + working-directory: backend + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Test with Maven + run: docker compose -f docker-compose.dev.yml run --rm backend-dev mvn test + + deploy: + needs: test + runs-on: ubuntu-latest + env: + working-directory: backend + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Docker Hub + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + + - name: Build Docker image + run: docker compose -f docker-compose.yml build + + - name: Tag Docker image + run: docker tag bounswe2024group1-backend:latest registry.digitalocean.com/semantic-browse/backend-staging:latest + + - name: Push Docker image + run: docker push registry.digitalocean.com/semantic-browse/backend-staging:latest + From 48777026c3cc0829d7acbee239dd6e539f99ecd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Tue, 7 May 2024 14:29:48 +0300 Subject: [PATCH 10/74] feature(frontend): add codegen to the frontend --- frontend/.eslintrc.cjs | 9 +- frontend/openapi-codegen.config.ts | 24 + frontend/package.json | 17 +- frontend/src/App.tsx | 6 +- frontend/src/components/SearchBar.test.tsx | 18 +- frontend/src/routes/login.test.tsx | 28 +- frontend/src/routes/login.tsx | 35 +- frontend/src/routes/search.tsx | 16 +- frontend/src/routes/signup.tsx | 32 +- .../services/api/semanticBrowseComponents.ts | 1457 +++++++++++++++++ .../src/services/api/semanticBrowseContext.ts | 106 ++ .../src/services/api/semanticBrowseFetcher.ts | 213 +++ .../services/api/semanticBrowseResponses.ts | 120 ++ .../src/services/api/semanticBrowseSchemas.ts | 218 +++ frontend/src/services/auth.tsx | 59 - frontend/src/services/query-client.ts | 3 + frontend/src/services/search.tsx | 21 - frontend/yarn.lock | 1273 +++++++++++++- swagger/openapi.yml | 245 +-- 19 files changed, 3635 insertions(+), 265 deletions(-) create mode 100644 frontend/openapi-codegen.config.ts create mode 100644 frontend/src/services/api/semanticBrowseComponents.ts create mode 100644 frontend/src/services/api/semanticBrowseContext.ts create mode 100644 frontend/src/services/api/semanticBrowseFetcher.ts create mode 100644 frontend/src/services/api/semanticBrowseResponses.ts create mode 100644 frontend/src/services/api/semanticBrowseSchemas.ts create mode 100644 frontend/src/services/query-client.ts delete mode 100644 frontend/src/services/search.tsx diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 96692a9d..6aac01dd 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -7,7 +7,14 @@ module.exports = { "plugin:react-hooks/recommended", "prettier", ], - ignorePatterns: ["dist", ".eslintrc.cjs"], + ignorePatterns: [ + "dist", + ".eslintrc.cjs", + // auto-generated files are ignored + "src/services/api/semanticBrowseResponses.ts", + "src/services/api/semanticBrowseComponents.ts", + "src/services/api/semanticBrowseSchemas.ts", + ], parser: "@typescript-eslint/parser", plugins: ["react-refresh"], rules: { diff --git a/frontend/openapi-codegen.config.ts b/frontend/openapi-codegen.config.ts new file mode 100644 index 00000000..8d6e75b6 --- /dev/null +++ b/frontend/openapi-codegen.config.ts @@ -0,0 +1,24 @@ +import { + generateSchemaTypes, + generateReactQueryComponents, +} from "@openapi-codegen/typescript"; +import { defineConfig } from "@openapi-codegen/cli"; +export default defineConfig({ + semanticBrowse: { + from: { + relativePath: "../swagger/openapi.yml", + source: "file", + }, + outputDir: "./src/services/api", + to: async (context) => { + const filenamePrefix = "semanticBrowse"; + const { schemasFiles } = await generateSchemaTypes(context, { + filenamePrefix, + }); + await generateReactQueryComponents(context, { + filenamePrefix, + schemasFiles, + }); + }, + }, +}); diff --git a/frontend/package.json b/frontend/package.json index f3dd28d9..c3d7e81c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,8 @@ "preview": "vite preview", "format": "prettier --write .", "test": "vitest", - "prepare": "cd .. && husky frontend/.husky" + "prepare": "cd .. && husky frontend/.husky", + "generate:api": "openapi-codegen gen semanticBrowse" }, "dependencies": { "@radix-ui/react-aspect-ratio": "^1.0.3", @@ -23,6 +24,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", + "@tanstack/react-query": "^5.35.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "country-emoji": "^1.5.6", @@ -37,6 +39,8 @@ "zustand": "^4.5.2" }, "devDependencies": { + "@openapi-codegen/cli": "^2.0.2", + "@openapi-codegen/typescript": "^8.0.2", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^15.0.5", "@types/node": "^20.12.7", @@ -58,6 +62,7 @@ "postcss": "^8.4.38", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.14", + "react-query-swagger": "^15.11.1", "tailwindcss": "^3.4.3", "typescript": "^5.2.2", "vite": "^5.2.0", @@ -67,14 +72,8 @@ "pre-commit": "lint-staged" }, "lint-staged": { - "*/**/*.{js,jsx,ts,tsx}": [ - "prettier --write", - "eslint --fix", - "eslint" - ], - "*/**/*.{json,css,md}": [ - "prettier --write" - ] + "*/**/*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix", "eslint"], + "*/**/*.{json,css,md}": ["prettier --write"] }, "packageManager": "yarn@4.1.1" } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 944147a1..702a8223 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,12 +1,16 @@ import { RouterProvider, createBrowserRouter } from "react-router-dom"; import { routeConfig } from "./routes"; import { FullscreenLoading } from "./components/FullscreenLoading"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { queryClient } from "./services/query-client"; const router = createBrowserRouter(routeConfig); function App() { return ( - } /> + + } /> + ); } diff --git a/frontend/src/components/SearchBar.test.tsx b/frontend/src/components/SearchBar.test.tsx index 139fcf0b..48866f55 100644 --- a/frontend/src/components/SearchBar.test.tsx +++ b/frontend/src/components/SearchBar.test.tsx @@ -1,21 +1,19 @@ -import { RouterProvider, createMemoryRouter } from "react-router-dom"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { routeConfig } from "../routes"; import { expect, test, vi } from "vitest"; +import { SearchBar } from "./SearchBar"; +import { useNavigate } from "react-router-dom"; -vi.mock("@/services/search", () => { +vi.mock("react-router-dom", () => { + const navigate = vi.fn(); return { - searchDishes: () => Promise.resolve([]), + useSearchParams: () => [new URLSearchParams("")], + useNavigate: () => navigate, }; }); test("searching something goes to /search", async () => { // Arrange - const router = createMemoryRouter(routeConfig, { - initialEntries: ["/"], - }); - - render(); + render(); // Act const search = screen.getAllByPlaceholderText("Search for dishes...")[0]; @@ -28,6 +26,6 @@ test("searching something goes to /search", async () => { // Assert await waitFor(() => { - expect(router.state.location.pathname).toBe("/search"); + expect(useNavigate()).toHaveBeenCalledWith("/search?q=hello"); }); }); diff --git a/frontend/src/routes/login.test.tsx b/frontend/src/routes/login.test.tsx index 815fcbf4..ab143267 100644 --- a/frontend/src/routes/login.test.tsx +++ b/frontend/src/routes/login.test.tsx @@ -1,17 +1,27 @@ import { RouterProvider, createMemoryRouter } from "react-router-dom"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { routeConfig } from "../routes"; -import { expect, test, vi } from "vitest"; -import { signin } from "@/services/auth"; +import { afterEach, expect, test, vi } from "vitest"; +import { fetchLogin } from "@/services/api/semanticBrowseComponents"; +import useAuthStore from "../services/auth"; -vi.mock("@/services/auth", async (importOriginal) => { - const mod = await importOriginal(); +vi.mock("@/services/api/semanticBrowseComponents", async (importOriginal) => { + const mod = + await importOriginal< + typeof import("@/services/api/semanticBrowseComponents") + >(); return { ...mod, - signin: vi.fn(() => Promise.resolve("token")), + fetchLogin: vi.fn(() => + Promise.resolve({ data: { token: "token" }, status: 200 }), + ), }; }); +afterEach(() => { + useAuthStore.setState(useAuthStore.getInitialState()); +}); + test("login calls service", async () => { // Arrange const router = createMemoryRouter(routeConfig, { @@ -30,9 +40,11 @@ test("login calls service", async () => { // Assert await waitFor(() => { - expect(signin).toHaveBeenCalledWith({ - usernameOrEmail: "efe", - password: "password", + expect(fetchLogin).toHaveBeenCalledWith({ + body: { + usernameOrEmail: "efe", + password: "password", + }, }); }); }); diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index dccdc068..008496d3 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -19,8 +19,14 @@ import { } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import useAuthStore, { signin } from "../services/auth"; +import useAuthStore from "../services/auth"; import { z } from "zod"; +import { fetchLogin } from "@/services/api/semanticBrowseComponents"; +import { + FetchError, + getFieldErrors, + renderError, +} from "../services/api/semanticBrowseFetcher"; const loginSchema = z.object({ usernameOrEmail: z.string().min(1, "Username or email is required"), @@ -46,24 +52,15 @@ export const action = async ({ } try { - await signin(parsed.data); - } catch (error) { - if ( - error && - typeof error === "object" && - "message" in error && - typeof error.message === "string" - ) { - return { - formErrors: [error.message], - fieldErrors: {}, - }; - } else { - return { - formErrors: ["An unknown error occurred"], - fieldErrors: {}, - }; + const response = await fetchLogin({ body: parsed.data }); + if (response.data.token) { + useAuthStore.getState().setToken(response.data.token); } + } catch (error) { + return { + formErrors: [renderError(error as FetchError)], + fieldErrors: getFieldErrors(error as FetchError), + }; } const redirectTo = formData.get("redirectTo") as string | null; @@ -136,7 +133,7 @@ export default function Login() {
Don't have an account?{" "} - + Sign up
diff --git a/frontend/src/routes/search.tsx b/frontend/src/routes/search.tsx index ef25bf5f..771ce96c 100644 --- a/frontend/src/routes/search.tsx +++ b/frontend/src/routes/search.tsx @@ -1,20 +1,26 @@ import { LoaderFunctionArgs, useLoaderData } from "react-router-dom"; import { Dish } from "../components/Dish"; -import { searchDishes } from "../services/search"; +import { fetchSearchDishes } from "../services/api/semanticBrowseComponents"; export const loader = ({ request }: LoaderFunctionArgs) => { const params = new URL(request.url).searchParams; - return searchDishes(params.get("q") || ""); + return fetchSearchDishes({ + queryParams: { q: params.get("q") ?? undefined }, + }); }; export const Search = () => { - const data = useLoaderData() as Awaited>; + const { data: searchResult } = useLoaderData() as Awaited< + ReturnType + >; return (
-

Found {data.length} results

+

+ Found {searchResult.length} results +

- {data.map((dish) => ( + {searchResult.map((dish) => ( ; + +export type LogoutResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +export type LogoutVariables = SemanticBrowseContext["fetcherOptions"]; + +export const fetchLogout = (variables: LogoutVariables, signal?: AbortSignal) => + semanticBrowseFetch({ + url: "/users/logout", + method: "post", + ...variables, + signal, + }); + +export const useLogout = ( + options?: Omit< + reactQuery.UseMutationOptions, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation({ + mutationFn: (variables: LogoutVariables) => + fetchLogout({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type VerifyEmailError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type VerifyEmailResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +export type VerifyEmailRequestBody = { + token: string; +}; + +export type VerifyEmailVariables = { + body: VerifyEmailRequestBody; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchVerifyEmail = ( + variables: VerifyEmailVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + VerifyEmailResponse, + VerifyEmailError, + VerifyEmailRequestBody, + {}, + {}, + {} + >({ url: "/users/verify-email", method: "post", ...variables, signal }); + +export const useVerifyEmail = ( + options?: Omit< + reactQuery.UseMutationOptions< + VerifyEmailResponse, + VerifyEmailError, + VerifyEmailVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + VerifyEmailResponse, + VerifyEmailError, + VerifyEmailVariables + >({ + mutationFn: (variables: VerifyEmailVariables) => + fetchVerifyEmail({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type ResendVerificationEmailError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type ResendVerificationEmailResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +export type ResendVerificationEmailVariables = + SemanticBrowseContext["fetcherOptions"]; + +export const fetchResendVerificationEmail = ( + variables: ResendVerificationEmailVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + ResendVerificationEmailResponse, + ResendVerificationEmailError, + undefined, + {}, + {}, + {} + >({ + url: "/users/resend-verification-email", + method: "post", + ...variables, + signal, + }); + +export const useResendVerificationEmail = ( + options?: Omit< + reactQuery.UseMutationOptions< + ResendVerificationEmailResponse, + ResendVerificationEmailError, + ResendVerificationEmailVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + ResendVerificationEmailResponse, + ResendVerificationEmailError, + ResendVerificationEmailVariables + >({ + mutationFn: (variables: ResendVerificationEmailVariables) => + fetchResendVerificationEmail({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type ResetPasswordRequestError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type ResetPasswordRequestResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +export type ResetPasswordRequestRequestBody = { + email: string; +}; + +export type ResetPasswordRequestVariables = { + body: ResetPasswordRequestRequestBody; +} & SemanticBrowseContext["fetcherOptions"]; + +/** + * A password reset link will be sent to the user's email if it exists. The response must always be success (even if email does not exist). This is to prevent email enumeration attacks. + */ +export const fetchResetPasswordRequest = ( + variables: ResetPasswordRequestVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + ResetPasswordRequestResponse, + ResetPasswordRequestError, + ResetPasswordRequestRequestBody, + {}, + {}, + {} + >({ + url: "/users/reset-password-request", + method: "post", + ...variables, + signal, + }); + +/** + * A password reset link will be sent to the user's email if it exists. The response must always be success (even if email does not exist). This is to prevent email enumeration attacks. + */ +export const useResetPasswordRequest = ( + options?: Omit< + reactQuery.UseMutationOptions< + ResetPasswordRequestResponse, + ResetPasswordRequestError, + ResetPasswordRequestVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + ResetPasswordRequestResponse, + ResetPasswordRequestError, + ResetPasswordRequestVariables + >({ + mutationFn: (variables: ResetPasswordRequestVariables) => + fetchResetPasswordRequest({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type ResetPasswordError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type ResetPasswordResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +export type ResetPasswordRequestBody = { + token: string; + newPassword: string; +}; + +export type ResetPasswordVariables = { + body: ResetPasswordRequestBody; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchResetPassword = ( + variables: ResetPasswordVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + ResetPasswordResponse, + ResetPasswordError, + ResetPasswordRequestBody, + {}, + {}, + {} + >({ url: "/users/reset-password", method: "post", ...variables, signal }); + +export const useResetPassword = ( + options?: Omit< + reactQuery.UseMutationOptions< + ResetPasswordResponse, + ResetPasswordError, + ResetPasswordVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + ResetPasswordResponse, + ResetPasswordError, + ResetPasswordVariables + >({ + mutationFn: (variables: ResetPasswordVariables) => + fetchResetPassword({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type SignupError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type SignupVariables = { + body: Schemas.UserRegistration; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchSignup = (variables: SignupVariables, signal?: AbortSignal) => + semanticBrowseFetch< + Responses.CreatedResponse, + SignupError, + Schemas.UserRegistration, + {}, + {}, + {} + >({ url: "/users/signup", method: "post", ...variables, signal }); + +export const useSignup = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.CreatedResponse, + SignupError, + SignupVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.CreatedResponse, + SignupError, + SignupVariables + >({ + mutationFn: (variables: SignupVariables) => + fetchSignup({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type LoginError = Fetcher.ErrorWrapper<{ + status: 401; + payload: Responses.UnauthorizedResponse; +}>; + +export type LoginResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.AuthToken; +}; + +export type LoginVariables = { + body: Schemas.UserLogin; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchLogin = (variables: LoginVariables, signal?: AbortSignal) => + semanticBrowseFetch( + { url: "/users/login", method: "post", ...variables, signal }, + ); + +export const useLogin = ( + options?: Omit< + reactQuery.UseMutationOptions, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation({ + mutationFn: (variables: LoginVariables) => + fetchLogin({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type GetUserByIdPathParams = { + userId: number; +}; + +export type GetUserByIdError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetUserByIdResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.UserProfile; +}; + +export type GetUserByIdVariables = { + pathParams: GetUserByIdPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetUserById = ( + variables: GetUserByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetUserByIdResponse, + GetUserByIdError, + undefined, + {}, + {}, + GetUserByIdPathParams + >({ url: "/users/{userId}", method: "get", ...variables, signal }); + +export const useGetUserById = ( + variables: GetUserByIdVariables, + options?: Omit< + reactQuery.UseQueryOptions, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ + path: "/users/{userId}", + operationId: "getUserById", + variables, + }), + queryFn: ({ signal }) => + fetchGetUserById({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type UpdateUserByIdPathParams = { + userId: number; +}; + +export type UpdateUserByIdError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type UpdateUserByIdResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.UserProfile; +}; + +export type UpdateUserByIdVariables = { + body?: Schemas.UserProfile; + pathParams: UpdateUserByIdPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchUpdateUserById = ( + variables: UpdateUserByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + UpdateUserByIdResponse, + UpdateUserByIdError, + Schemas.UserProfile, + {}, + {}, + UpdateUserByIdPathParams + >({ url: "/users/{userId}", method: "put", ...variables, signal }); + +export const useUpdateUserById = ( + options?: Omit< + reactQuery.UseMutationOptions< + UpdateUserByIdResponse, + UpdateUserByIdError, + UpdateUserByIdVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + UpdateUserByIdResponse, + UpdateUserByIdError, + UpdateUserByIdVariables + >({ + mutationFn: (variables: UpdateUserByIdVariables) => + fetchUpdateUserById({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type GetUserFollowingPathParams = { + userId: number; +}; + +export type GetUserFollowingError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type GetUserFollowingResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.UserSummary[]; +}; + +export type GetUserFollowingVariables = { + pathParams: GetUserFollowingPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetUserFollowing = ( + variables: GetUserFollowingVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetUserFollowingResponse, + GetUserFollowingError, + undefined, + {}, + {}, + GetUserFollowingPathParams + >({ url: "/users/{userId}/following", method: "get", ...variables, signal }); + +export const useGetUserFollowing = ( + variables: GetUserFollowingVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetUserFollowingResponse, + GetUserFollowingError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetUserFollowingResponse, + GetUserFollowingError, + TData + >({ + queryKey: queryKeyFn({ + path: "/users/{userId}/following", + operationId: "getUserFollowing", + variables, + }), + queryFn: ({ signal }) => + fetchGetUserFollowing({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type GetUserFollowersPathParams = { + userId: number; +}; + +export type GetUserFollowersError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type GetUserFollowersResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.UserSummary[]; +}; + +export type GetUserFollowersVariables = { + pathParams: GetUserFollowersPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetUserFollowers = ( + variables: GetUserFollowersVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetUserFollowersResponse, + GetUserFollowersError, + undefined, + {}, + {}, + GetUserFollowersPathParams + >({ url: "/users/{userId}/followers", method: "get", ...variables, signal }); + +export const useGetUserFollowers = ( + variables: GetUserFollowersVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetUserFollowersResponse, + GetUserFollowersError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetUserFollowersResponse, + GetUserFollowersError, + TData + >({ + queryKey: queryKeyFn({ + path: "/users/{userId}/followers", + operationId: "getUserFollowers", + variables, + }), + queryFn: ({ signal }) => + fetchGetUserFollowers({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type FollowUserError = Fetcher.ErrorWrapper< + | { + status: 400; + payload: Responses.BadRequestResponse; + } + | { + status: 404; + payload: Responses.NotFoundResponse; + } +>; + +export type FollowUserRequestBody = { + followingUserId?: number; +}; + +export type FollowUserVariables = { + body?: FollowUserRequestBody; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchFollowUser = ( + variables: FollowUserVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + FollowUserError, + FollowUserRequestBody, + {}, + {}, + {} + >({ url: "/users/follow", method: "post", ...variables, signal }); + +export const useFollowUser = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + FollowUserError, + FollowUserVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + FollowUserError, + FollowUserVariables + >({ + mutationFn: (variables: FollowUserVariables) => + fetchFollowUser({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type UnfollowUserError = Fetcher.ErrorWrapper< + | { + status: 400; + payload: Responses.BadRequestResponse; + } + | { + status: 404; + payload: Responses.NotFoundResponse; + } +>; + +export type UnfollowUserRequestBody = { + followingUserId?: number; +}; + +export type UnfollowUserVariables = { + body?: UnfollowUserRequestBody; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchUnfollowUser = ( + variables: UnfollowUserVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + UnfollowUserError, + UnfollowUserRequestBody, + {}, + {}, + {} + >({ url: "/users/unfollow", method: "post", ...variables, signal }); + +export const useUnfollowUser = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + UnfollowUserError, + UnfollowUserVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + UnfollowUserError, + UnfollowUserVariables + >({ + mutationFn: (variables: UnfollowUserVariables) => + fetchUnfollowUser({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type SearchUsersQueryParams = { + q?: string; +}; + +export type SearchUsersError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type SearchUsersResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.UserSummary[]; +}; + +export type SearchUsersVariables = { + queryParams?: SearchUsersQueryParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchSearchUsers = ( + variables: SearchUsersVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + SearchUsersResponse, + SearchUsersError, + undefined, + {}, + SearchUsersQueryParams, + {} + >({ url: "/search/users", method: "get", ...variables, signal }); + +export const useSearchUsers = ( + variables: SearchUsersVariables, + options?: Omit< + reactQuery.UseQueryOptions, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ + path: "/search/users", + operationId: "searchUsers", + variables, + }), + queryFn: ({ signal }) => + fetchSearchUsers({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type SearchDishesQueryParams = { + q?: string; + cuisine?: string; + foodType?: string; +}; + +export type SearchDishesError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type SearchDishesResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.DishDetails[]; +}; + +export type SearchDishesVariables = { + queryParams?: SearchDishesQueryParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchSearchDishes = ( + variables: SearchDishesVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + SearchDishesResponse, + SearchDishesError, + undefined, + {}, + SearchDishesQueryParams, + {} + >({ url: "/search/dishes", method: "get", ...variables, signal }); + +export const useSearchDishes = ( + variables: SearchDishesVariables, + options?: Omit< + reactQuery.UseQueryOptions, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ + path: "/search/dishes", + operationId: "searchDishes", + variables, + }), + queryFn: ({ signal }) => + fetchSearchDishes({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type GetDishByIdPathParams = { + dishId: number; +}; + +export type GetDishByIdError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetDishByIdResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.DishDetails; +}; + +export type GetDishByIdVariables = { + pathParams: GetDishByIdPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetDishById = ( + variables: GetDishByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetDishByIdResponse, + GetDishByIdError, + undefined, + {}, + {}, + GetDishByIdPathParams + >({ url: "/dishes/{dishId}", method: "get", ...variables, signal }); + +export const useGetDishById = ( + variables: GetDishByIdVariables, + options?: Omit< + reactQuery.UseQueryOptions, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ + path: "/dishes/{dishId}", + operationId: "getDishById", + variables, + }), + queryFn: ({ signal }) => + fetchGetDishById({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type GetCuisineByIdPathParams = { + cuisineId: number; +}; + +export type GetCuisineByIdQueryParams = { + includeDishes?: boolean; +}; + +export type GetCuisineByIdError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetCuisineByIdResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.CuisineDetails; +}; + +export type GetCuisineByIdVariables = { + pathParams: GetCuisineByIdPathParams; + queryParams?: GetCuisineByIdQueryParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetCuisineById = ( + variables: GetCuisineByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetCuisineByIdResponse, + GetCuisineByIdError, + undefined, + {}, + GetCuisineByIdQueryParams, + GetCuisineByIdPathParams + >({ url: "/cuisines/{cuisineId}", method: "get", ...variables, signal }); + +export const useGetCuisineById = ( + variables: GetCuisineByIdVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetCuisineByIdResponse, + GetCuisineByIdError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetCuisineByIdResponse, + GetCuisineByIdError, + TData + >({ + queryKey: queryKeyFn({ + path: "/cuisines/{cuisineId}", + operationId: "getCuisineById", + variables, + }), + queryFn: ({ signal }) => + fetchGetCuisineById({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type GetRecipesForEntityQueryParams = { + sort?: "recent" | "topRated"; + dishId?: number; + cuisineId?: number; +}; + +export type GetRecipesForEntityError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type GetRecipesForEntityResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.RecipeSummary[]; +}; + +export type GetRecipesForEntityVariables = { + queryParams?: GetRecipesForEntityQueryParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetRecipesForEntity = ( + variables: GetRecipesForEntityVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetRecipesForEntityResponse, + GetRecipesForEntityError, + undefined, + {}, + GetRecipesForEntityQueryParams, + {} + >({ url: "/recipes", method: "get", ...variables, signal }); + +export const useGetRecipesForEntity = ( + variables: GetRecipesForEntityVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetRecipesForEntityResponse, + GetRecipesForEntityError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetRecipesForEntityResponse, + GetRecipesForEntityError, + TData + >({ + queryKey: queryKeyFn({ + path: "/recipes", + operationId: "getRecipesForEntity", + variables, + }), + queryFn: ({ signal }) => + fetchGetRecipesForEntity({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type CreateRecipeError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type CreateRecipeResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.RecipeDetails; +}; + +export type CreateRecipeVariables = { + body: Schemas.NewRecipe; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchCreateRecipe = ( + variables: CreateRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + CreateRecipeResponse, + CreateRecipeError, + Schemas.NewRecipe, + {}, + {}, + {} + >({ url: "/recipes", method: "post", ...variables, signal }); + +export const useCreateRecipe = ( + options?: Omit< + reactQuery.UseMutationOptions< + CreateRecipeResponse, + CreateRecipeError, + CreateRecipeVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + CreateRecipeResponse, + CreateRecipeError, + CreateRecipeVariables + >({ + mutationFn: (variables: CreateRecipeVariables) => + fetchCreateRecipe({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type GetRecipeByIdPathParams = { + recipeId: number; +}; + +export type GetRecipeByIdError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetRecipeByIdResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.RecipeDetails; +}; + +export type GetRecipeByIdVariables = { + pathParams: GetRecipeByIdPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetRecipeById = ( + variables: GetRecipeByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetRecipeByIdResponse, + GetRecipeByIdError, + undefined, + {}, + {}, + GetRecipeByIdPathParams + >({ url: "/recipes/{recipeId}", method: "get", ...variables, signal }); + +export const useGetRecipeById = ( + variables: GetRecipeByIdVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetRecipeByIdResponse, + GetRecipeByIdError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ + path: "/recipes/{recipeId}", + operationId: "getRecipeById", + variables, + }), + queryFn: ({ signal }) => + fetchGetRecipeById({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type DeleteRecipeByIdPathParams = { + recipeId: number; +}; + +export type DeleteRecipeByIdError = Fetcher.ErrorWrapper< + | { + status: 403; + payload: Responses.ForbiddenResponse; + } + | { + status: 404; + payload: Responses.NotFoundResponse; + } +>; + +export type DeleteRecipeByIdVariables = { + pathParams: DeleteRecipeByIdPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchDeleteRecipeById = ( + variables: DeleteRecipeByIdVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + DeleteRecipeByIdError, + undefined, + {}, + {}, + DeleteRecipeByIdPathParams + >({ url: "/recipes/{recipeId}", method: "delete", ...variables, signal }); + +export const useDeleteRecipeById = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + DeleteRecipeByIdError, + DeleteRecipeByIdVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + DeleteRecipeByIdError, + DeleteRecipeByIdVariables + >({ + mutationFn: (variables: DeleteRecipeByIdVariables) => + fetchDeleteRecipeById({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type RateRecipePathParams = { + recipeId: number; +}; + +export type RateRecipeError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type RateRecipeRequestBody = { + /** + * @minimum 1 + * @maximum 5 + */ + rating?: number; +}; + +export type RateRecipeVariables = { + body?: RateRecipeRequestBody; + pathParams: RateRecipePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchRateRecipe = ( + variables: RateRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + RateRecipeError, + RateRecipeRequestBody, + {}, + {}, + RateRecipePathParams + >({ + url: "/recipes/{recipeId}/rating", + method: "post", + ...variables, + signal, + }); + +export const useRateRecipe = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + RateRecipeError, + RateRecipeVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + RateRecipeError, + RateRecipeVariables + >({ + mutationFn: (variables: RateRecipeVariables) => + fetchRateRecipe({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type BookmarkRecipePathParams = { + recipeId: number; +}; + +export type BookmarkRecipeError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type BookmarkRecipeVariables = { + pathParams: BookmarkRecipePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchBookmarkRecipe = ( + variables: BookmarkRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + BookmarkRecipeError, + undefined, + {}, + {}, + BookmarkRecipePathParams + >({ + url: "/recipes/{recipeId}/bookmark", + method: "post", + ...variables, + signal, + }); + +export const useBookmarkRecipe = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + BookmarkRecipeError, + BookmarkRecipeVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + BookmarkRecipeError, + BookmarkRecipeVariables + >({ + mutationFn: (variables: BookmarkRecipeVariables) => + fetchBookmarkRecipe({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type GetFeedQueryParams = { + type: "explore" | "following"; +}; + +export type GetFeedError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type GetFeedResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | Schemas.RecipeSummary[]; +}; + +export type GetFeedVariables = { + queryParams: GetFeedQueryParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetFeed = ( + variables: GetFeedVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetFeedResponse, + GetFeedError, + undefined, + {}, + GetFeedQueryParams, + {} + >({ url: "/feed", method: "get", ...variables, signal }); + +export const useGetFeed = ( + variables: GetFeedVariables, + options?: Omit< + reactQuery.UseQueryOptions, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery({ + queryKey: queryKeyFn({ path: "/feed", operationId: "getFeed", variables }), + queryFn: ({ signal }) => + fetchGetFeed({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type QueryOperation = + | { + path: "/users/{userId}"; + operationId: "getUserById"; + variables: GetUserByIdVariables; + } + | { + path: "/users/{userId}/following"; + operationId: "getUserFollowing"; + variables: GetUserFollowingVariables; + } + | { + path: "/users/{userId}/followers"; + operationId: "getUserFollowers"; + variables: GetUserFollowersVariables; + } + | { + path: "/search/users"; + operationId: "searchUsers"; + variables: SearchUsersVariables; + } + | { + path: "/search/dishes"; + operationId: "searchDishes"; + variables: SearchDishesVariables; + } + | { + path: "/dishes/{dishId}"; + operationId: "getDishById"; + variables: GetDishByIdVariables; + } + | { + path: "/cuisines/{cuisineId}"; + operationId: "getCuisineById"; + variables: GetCuisineByIdVariables; + } + | { + path: "/recipes"; + operationId: "getRecipesForEntity"; + variables: GetRecipesForEntityVariables; + } + | { + path: "/recipes/{recipeId}"; + operationId: "getRecipeById"; + variables: GetRecipeByIdVariables; + } + | { + path: "/feed"; + operationId: "getFeed"; + variables: GetFeedVariables; + }; diff --git a/frontend/src/services/api/semanticBrowseContext.ts b/frontend/src/services/api/semanticBrowseContext.ts new file mode 100644 index 00000000..b5b1b202 --- /dev/null +++ b/frontend/src/services/api/semanticBrowseContext.ts @@ -0,0 +1,106 @@ +import type { QueryKey, UseQueryOptions } from "@tanstack/react-query"; +import { QueryOperation } from "./semanticBrowseComponents"; + +export type SemanticBrowseContext = { + fetcherOptions: { + /** + * Headers to inject in the fetcher + */ + headers?: object; + /** + * Query params to inject in the fetcher + */ + queryParams?: object; + }; + queryOptions: { + /** + * Set this to `false` to disable automatic refetching when the query mounts or changes query keys. + * Defaults to `true`. + */ + enabled?: boolean; + }; + /** + * Query key manager. + */ + queryKeyFn: (operation: QueryOperation) => QueryKey; +}; + +/** + * Context injected into every react-query hook wrappers + * + * @param queryOptions options from the useQuery wrapper + */ +export function useSemanticBrowseContext< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + // eslint-disable-next-line + _queryOptions?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + >, +): SemanticBrowseContext { + return { + fetcherOptions: {}, + queryOptions: {}, + queryKeyFn, + }; +} + +export const queryKeyFn = (operation: QueryOperation) => { + const queryKey: unknown[] = hasPathParams(operation) + ? operation.path + .split("/") + .filter(Boolean) + .map((i) => resolvePathParam(i, operation.variables.pathParams)) + : operation.path.split("/").filter(Boolean); + + if (hasQueryParams(operation)) { + queryKey.push(operation.variables.queryParams); + } + + if (hasBody(operation)) { + queryKey.push(operation.variables.body); + } + + return queryKey; +}; +// Helpers +const resolvePathParam = (key: string, pathParams: Record) => { + if (key.startsWith("{") && key.endsWith("}")) { + return pathParams[key.slice(1, -1)]; + } + return key; +}; + +const hasPathParams = ( + operation: QueryOperation, +): operation is QueryOperation & { + variables: { pathParams: Record }; +} => { + return ( + "pathParams" in operation.variables && + Boolean(operation.variables.pathParams) + ); +}; + +const hasBody = ( + operation: QueryOperation, +): operation is QueryOperation & { + variables: { body: Record }; +} => { + return "body" in operation.variables && Boolean(operation.variables.body); +}; + +const hasQueryParams = ( + operation: QueryOperation, +): operation is QueryOperation & { + variables: { queryParams: Record }; +} => { + return ( + "queryParams" in operation.variables && + Boolean(operation.variables.queryParams) + ); +}; diff --git a/frontend/src/services/api/semanticBrowseFetcher.ts b/frontend/src/services/api/semanticBrowseFetcher.ts new file mode 100644 index 00000000..6e6b42b9 --- /dev/null +++ b/frontend/src/services/api/semanticBrowseFetcher.ts @@ -0,0 +1,213 @@ +import { SemanticBrowseContext } from "./semanticBrowseContext"; +import { + ApiResponse, + ErrorResponseObject, + SuccessResponseObject, +} from "./semanticBrowseSchemas"; + +const baseUrl = "/api/v1"; + +export type ErrorWrapper = + | TError + | { status: "unknown"; payload: ErrorResponseObject }; + +export type FetchError = ErrorWrapper<{ + status: number | "unknown"; + payload: ErrorResponseObject; +}>; + +export type SemanticBrowseFetcherOptions< + TBody, + THeaders, + TQueryParams, + TPathParams, +> = { + url: string; + method: string; + body?: TBody; + headers?: THeaders; + queryParams?: TQueryParams; + pathParams?: TPathParams; + signal?: AbortSignal; +} & SemanticBrowseContext["fetcherOptions"]; + +type CleanupInnerData = + // remove inner record + { + status: TData["status"]; + data: Extract extends never + ? TData["data"] + : Extract; + }; + +export async function semanticBrowseFetch< + TData extends SuccessResponseObject, + TError extends { status: number | "unknown"; payload: ErrorResponseObject }, + // eslint-disable-next-line + TBody extends any | FormData | undefined | null, + THeaders extends Record, + TQueryParams extends Record, + TPathParams extends Record, +>({ + url, + method, + body, + headers, + pathParams, + queryParams, + signal, +}: SemanticBrowseFetcherOptions< + TBody, + THeaders, + TQueryParams, + TPathParams +>): Promise> { + try { + const requestHeaders: HeadersInit = { + "Content-Type": "application/json", + ...headers, + }; + + /** + * As the fetch API is being used, when multipart/form-data is specified + * the Content-Type header must be deleted so that the browser can set + * the correct boundary. + * https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#sending_files_using_a_formdata_object + */ + if ( + requestHeaders["Content-Type"] + .toLowerCase() + .includes("multipart/form-data") + ) { + delete requestHeaders["Content-Type"]; + } + + const response = await window.fetch( + `${baseUrl}${resolveUrl(url, queryParams, pathParams)}`, + { + signal, + method: method.toUpperCase(), + body: body + ? body instanceof FormData + ? body + : JSON.stringify(body) + : undefined, + headers: requestHeaders, + }, + ); + if (!response.ok) { + let error: ErrorWrapper; + try { + error = { + status: response.status, + payload: (await response.json()) as ErrorResponseObject, + } as ErrorWrapper; + } catch (e) { + error = { + status: "unknown", + payload: { + status: 500, + errors: [ + { + message: + e instanceof Error + ? `Unexpected error (${e.message})` + : "Unexpected error", + }, + ], + }, + }; + } + + throw error; + } + + if (response.headers.get("content-type")?.includes("json")) { + const data: ApiResponse = await response.json(); + if (data.status >= 400) { + throw { + status: data.status, + payload: data, + }; + } + + return data as CleanupInnerData; + } else { + // if it is not a json response, assume it is a blob and cast it to TData + return (await response.blob()) as unknown as CleanupInnerData; + } + } catch (e) { + if (e instanceof Error) { + const errorObject: ErrorResponseObject = { + status: 500, + errors: [ + { + message: + e instanceof Error + ? `Network error (${e.message})` + : "Network error", + }, + ], + }; + throw errorObject; + } else throw e; + } +} + +const resolveUrl = ( + url: string, + queryParams: Record = {}, + pathParams: Record = {}, +) => { + let query = new URLSearchParams( + queryParams as Record, + ).toString(); + if (query) query = `?${query}`; + return ( + url.replace(/\{\w*\}/g, (key) => String(pathParams[key.slice(1, -1)])) + + query + ); +}; + +export const renderError = ( + error: ErrorWrapper<{ status: unknown; payload: ErrorResponseObject }>, +): string => { + if (!("errors" in error.payload)) { + return error.payload?.["message"] ?? "Unknown error"; + } + const errors = error.payload.errors; + + const fieldErrors = errors.filter((e) => !!e.field); + const generalErrors = errors.filter((e) => !e.field); + const renderedString = + errors.length > 0 + ? generalErrors.join("\n") + + "\n" + + (fieldErrors.length ? "Field errors:\n" + fieldErrors.join("\n") : "") + : "Unknown error."; + + return renderedString; +}; + +export const getFieldErrors = ( + error: ErrorWrapper<{ status: unknown; payload: ErrorResponseObject }>, +): Record => { + if (!("errors" in error.payload)) { + return {}; + } + const errors = error.payload.errors; + + const fieldErrors = errors.filter((e) => !!e.field); + return fieldErrors.reduce( + (acc, item) => { + if (item.field! in acc) { + acc[item.field!] += "\n" + item.message; + } else { + acc[item.field!] = item.message; + } + + return acc; + }, + {} as Record, + ); +}; diff --git a/frontend/src/services/api/semanticBrowseResponses.ts b/frontend/src/services/api/semanticBrowseResponses.ts new file mode 100644 index 00000000..c76ca975 --- /dev/null +++ b/frontend/src/services/api/semanticBrowseResponses.ts @@ -0,0 +1,120 @@ +/** + * Generated by @openapi-codegen + * + * @version 1.0.2 + */ +import type * as Schemas from "./semanticBrowseSchemas"; + +export type OkResponse = Schemas.SuccessResponseObject; + +/** + * OK + */ +export type CreatedResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +/** + * Response with errors + */ +export type BadRequestResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: Schemas.ApiError[]; +}; + +/** + * Response with errors + */ +export type UnauthorizedResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: Schemas.ApiError[]; +}; + +/** + * Response with errors + */ +export type ForbiddenResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: Schemas.ApiError[]; +}; + +/** + * Response with errors + */ +export type NotFoundResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: Schemas.ApiError[]; +}; + +/** + * Response with errors + */ +export type ConflictResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: Schemas.ApiError[]; +}; + +export type InternalServerErrorResponse = { + /** + * @example 500 + */ + status?: number; + errors?: Schemas.ApiError[]; +}; diff --git a/frontend/src/services/api/semanticBrowseSchemas.ts b/frontend/src/services/api/semanticBrowseSchemas.ts new file mode 100644 index 00000000..b7a10b0a --- /dev/null +++ b/frontend/src/services/api/semanticBrowseSchemas.ts @@ -0,0 +1,218 @@ +/** + * Generated by @openapi-codegen + * + * @version 1.0.2 + */ +export type UserRegistration = { + email: string; + username: string; + password: string; + bio?: string; + firstName: string; + lastName: string; + country: string; +}; + +export type UserLogin = { + usernameOrEmail: string; + password: string; +}; + +export type AuthToken = { + token?: string; +}; + +/** + * @example {"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","bio":"I love takoyaki!","followersCount":100,"gender":"unknown","profilePicture":"https://images.unsplash.com/photo-1633790450512-98e68a55ef15?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=brunno-tozzo-GAIC2WHxm5A-unsplash.jpg&w=640","diets":["keto"],"recipeCount":10,"bookmarks":[{"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","cookTime":30,"images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"rating":4.5,"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki"}}],"recipes":[{"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","cookTime":30,"images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"rating":4.5,"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki"}}]} + */ +export type UserProfile = { + id?: number; + username?: string; + name?: string; + bio?: string; + followersCount?: number; + gender?: "male" | "female" | "unknown"; + /** + * @format uri + */ + profilePicture?: string; + diets?: string[]; + recipeCount?: number; + /** + * Only available when querying own profile. + */ + bookmarks?: RecipeSummary[]; + recipes?: RecipeSummary[]; +}; + +/** + * @example {"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","followersCount":100,"profilePicture":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","recipeCount":10,"avgRating":4.5} + */ +export type UserSummary = { + id?: number; + username?: string; + name?: string; + followersCount?: number; + /** + * @format uri + */ + profilePicture?: string; + recipeCount?: number; + /** + * @format float + */ + avgRating?: number; +}; + +/** + * @example {"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki","description":"ball-shaped Japanese snack with octopus","image":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","countries":"Japan","ingredients":"wheat flour, green laver, octopus as food, beni shōga, katsuobushi, Welsh onion","foodTypes":"konamono, octopus dish, yakimono","cuisines":"Japanese cuisine"} + */ +export type DishDetails = { + id: string; + name: string; + description: string; + /** + * @format uri + */ + image: string; + countries: string; + ingredients?: string; + foodTypes?: string; + cuisine?: CuisineSummary; + /** + * Only returned when directly querying a dish. + */ + recipes?: RecipeSummary[]; +}; + +export type CuisineDetails = { + id: string; + name: string; + description: string; + /** + * @format uri + */ + image: string; + dishes?: DishSummary[]; +}; + +/** + * @example {"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","cookTime":"30 minutes","images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"rating":4.5,"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki","image":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"},"author":{"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","followersCount":100,"profilePicture":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","recipeCount":10,"avgRating":4.5}} + */ +export type RecipeSummary = { + id: number; + name: string; + description: string; + /** + * Cook time in minutes + */ + cookTime?: number; + images: string[]; + /** + * @format float + */ + rating: number; + dish?: DishSummary; + author: UserSummary; +}; + +/** + * @example {"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","instructions":["Mix the batter.","Add the octopus.","Cook the takoyaki balls."],"ingredients":["wheat flour","green laver","octopus as food","beni shōga","katsuobushi","Welsh onion"],"images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"cookTime":"30 minutes","servingSize":4,"allergens":["seafood"],"cuisine":{"id":1,"name":"Japanese"},"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki","image":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"},"avgRating":4.5,"ratingsCount":10} + */ +export type RecipeDetails = { + id: number; + name: string; + description: string; + instructions: string[]; + ingredients: string[]; + images: string[]; + cookTime: string; + servingSize: number; + allergens: string[]; + cuisine?: CuisineSummary; + dish?: DishSummary; + /** + * @format float + */ + avgRating?: number; + ratingsCount: number; + author: UserSummary; +}; + +/** + * @example {"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","instructions":["Mix the batter.","Add the octopus.","Cook the takoyaki balls."],"ingredients":["wheat flour","green laver","octopus as food","beni shōga","katsuobushi","Welsh onion"],"images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"prepTime":"10 minutes","cookTime":"30 minutes","servingSize":4,"allergens":["seafood"],"dishId":1} + */ +export type NewRecipe = { + name: string; + description?: string; + instructions: string[]; + ingredients: string[]; + images?: Blob[]; + prepTime: string; + cookTime: string; + servingSize: number; + allergens?: string[]; + dishId?: number; +}; + +/** + * @example {"id":1,"name":"Japanese"} + */ +export type CuisineSummary = { + id: number; + name: string; +}; + +/** + * @example {"id":1,"name":"takoyaki"} + */ +export type DishSummary = { + id: number; + name: string; +}; + +/** + * @example {"status":200,"data":{"message":"Success"}} + * @example {"status":400,"errors":[{"message":"Invalid email","field":"email"},{"message":"Invalid password","field":"password"}]} + */ +export type ApiResponse = SuccessResponseObject | ErrorResponseObject; + +export type ApiError = { + message: string; + /** + * If empty, indicates an error not related to any field. + */ + field?: string; +}; + +/** + * OK + */ +export type SuccessResponseObject = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Record | any[]; +}; + +/** + * Response with errors + */ +export type ErrorResponseObject = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * + * @example 400 + * @example 401 + * @example 403 + * @example 404 + * @example 409 + * @example 500 + */ + status: 400 | 401 | 403 | 404 | 409 | 500; + errors: ApiError[]; +}; diff --git a/frontend/src/services/auth.tsx b/frontend/src/services/auth.tsx index bfabdab9..f3a017c4 100644 --- a/frontend/src/services/auth.tsx +++ b/frontend/src/services/auth.tsx @@ -23,65 +23,6 @@ const useAuthStore = create()( export default useAuthStore; -interface UserLogin { - usernameOrEmail: string; - password: string; -} -/* - * Returns token if successful - * Throws an error if unsuccessful - */ -export const signin = async (login: UserLogin): Promise => { - return fetch("/api/v1/users/login", { - method: "POST", - body: JSON.stringify(login), - headers: { "Content-Type": "application/json" }, - }) - .then((response) => response.json()) - .then((data) => { - if (data.status !== 200) { - throw new Error(data.message); - } - useAuthStore.getState().setToken(data.data.token); - }) - .catch((error: Error) => { - if (error.message) { - throw new Error(error.message); - } - throw new Error("Unknown error"); - }); -}; - export const signout = async (): Promise => { useAuthStore.getState().setToken(null); }; - -interface SignupRequest { - username: string; - email: string; - password: string; - firstName: string; - lastName: string; - country: string; -} - -export const signup = async (signup: SignupRequest): Promise => { - return fetch("/api/v1/users/signup", { - method: "POST", - body: JSON.stringify(signup), - headers: { "Content-Type": "application/json" }, - }) - .then((response) => response.json()) - .then((data) => { - if (data.status !== 200) { - throw new Error(data.message); - } - useAuthStore.getState().setToken(data.data.token); - }) - .catch((error: Error) => { - if (error.message) { - throw new Error(error.message); - } - throw new Error("Unknown error"); - }); -}; diff --git a/frontend/src/services/query-client.ts b/frontend/src/services/query-client.ts new file mode 100644 index 00000000..6c7b9ded --- /dev/null +++ b/frontend/src/services/query-client.ts @@ -0,0 +1,3 @@ +import { QueryClient } from "@tanstack/react-query"; + +export const queryClient = new QueryClient(); diff --git a/frontend/src/services/search.tsx b/frontend/src/services/search.tsx deleted file mode 100644 index e1105e33..00000000 --- a/frontend/src/services/search.tsx +++ /dev/null @@ -1,21 +0,0 @@ -interface Dish { - id: string; - name: string; - description: string; - countries: string; - ingredients: string; - foodTypes: string; - cuisines: string; - image: string; -} - -export const searchDishes = async (query: string): Promise => { - try { - const res = await fetch( - "/api/v1/search/dishes?q=" + encodeURIComponent(query), - ); - return (await res.json()).data; - } catch { - throw new Error("Failed to fetch dishes"); - } -}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1e6ad33a..64495f0e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -19,6 +19,43 @@ __metadata: languageName: node linkType: hard +"@apollo/client@npm:^3.5.10": + version: 3.10.2 + resolution: "@apollo/client@npm:3.10.2" + dependencies: + "@graphql-typed-document-node/core": "npm:^3.1.1" + "@wry/caches": "npm:^1.0.0" + "@wry/equality": "npm:^0.5.6" + "@wry/trie": "npm:^0.5.0" + graphql-tag: "npm:^2.12.6" + hoist-non-react-statics: "npm:^3.3.2" + optimism: "npm:^0.18.0" + prop-types: "npm:^15.7.2" + rehackt: "npm:^0.1.0" + response-iterator: "npm:^0.2.6" + symbol-observable: "npm:^4.0.0" + ts-invariant: "npm:^0.10.3" + tslib: "npm:^2.3.0" + zen-observable-ts: "npm:^1.2.5" + peerDependencies: + graphql: ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + checksum: 10c0/b0ef2dfbf72494442a037bd74a6d01bcfc37883b121410c61d97766c1383e573c7aa38b298b890f723f018f0c0eb142d4d8a65d25f465085df6d3fa5930826d1 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.10.4": version: 7.24.2 resolution: "@babel/code-frame@npm:7.24.2" @@ -303,6 +340,13 @@ __metadata: languageName: node linkType: hard +"@exodus/schemasafe@npm:^1.0.0-rc.2": + version: 1.3.0 + resolution: "@exodus/schemasafe@npm:1.3.0" + checksum: 10c0/e19397c14db76342154c32a9088536149babfd9b18ecae815add0b2f911d9aa292aa51c6ab33b857b4b6bb371a74ebde845e6f17b2824e73b4e307230f23f86a + languageName: node + linkType: hard + "@floating-ui/core@npm:^1.0.0": version: 1.6.1 resolution: "@floating-ui/core@npm:1.6.1" @@ -341,6 +385,15 @@ __metadata: languageName: node linkType: hard +"@graphql-typed-document-node/core@npm:^3.1.1": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -480,6 +533,51 @@ __metadata: languageName: node linkType: hard +"@openapi-codegen/cli@npm:^2.0.2": + version: 2.0.2 + resolution: "@openapi-codegen/cli@npm:2.0.2" + dependencies: + "@apollo/client": "npm:^3.5.10" + "@swc/core": "npm:^1.2.118" + case: "npm:^1.6.3" + chalk: "npm:^5.0.0" + cli-highlight: "npm:^2.1.11" + clipanion: "npm:^3.2.0-rc.10" + fs-extra: "npm:^10.0.0" + got: "npm:^12.0.0" + got-fetch: "npm:^5.1.1" + graphql: "npm:^15.8.0" + ink: "npm:^3.2.0" + js-yaml: "npm:^4.1.0" + openapi3-ts: "npm:^2.0.1" + prettier: "npm:^3.2.5" + rxjs: "npm:^7.5.4" + slash: "npm:^4.0.0" + swagger2openapi: "npm:^7.0.8" + tslib: "npm:^2.3.1" + typanion: "npm:^3.7.1" + typescript: "npm:4.8.2" + bin: + openapi-codegen: lib/cli.js + checksum: 10c0/8ec9f21d790c05e0bf9cf49691036c5b1f17a3a0cab93a963ce11f6a8a2d2967599e830f9710ac45e5476423ea9b9f9840195e7a5c53ad0badc1aa68c10a5f17 + languageName: node + linkType: hard + +"@openapi-codegen/typescript@npm:^8.0.2": + version: 8.0.2 + resolution: "@openapi-codegen/typescript@npm:8.0.2" + dependencies: + case: "npm:^1.6.3" + lodash: "npm:^4.17.21" + openapi3-ts: "npm:^2.0.1" + pluralize: "npm:^8.0.0" + tslib: "npm:^2.3.1" + tsutils: "npm:^3.21.0" + typescript: "npm:4.8.2" + checksum: 10c0/7082725bf8db24878472360629f19b3599aae0ab3c426e001d4b4988bf37ed37524200d204d2d72f4b3cd37d636d4eb862c46806e19bd82b56013484f83c4dc3 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -1194,6 +1292,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/is@npm:^5.2.0": + version: 5.6.0 + resolution: "@sindresorhus/is@npm:5.6.0" + checksum: 10c0/66727344d0c92edde5760b5fd1f8092b717f2298a162a5f7f29e4953e001479927402d9d387e245fb9dc7d3b37c72e335e93ed5875edfc5203c53be8ecba1b52 + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-darwin-arm64@npm:1.4.17" @@ -1201,6 +1306,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-arm64@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-darwin-arm64@npm:1.5.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-darwin-x64@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-darwin-x64@npm:1.4.17" @@ -1208,6 +1320,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-x64@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-darwin-x64@npm:1.5.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@swc/core-linux-arm-gnueabihf@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.17" @@ -1215,6 +1334,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm-gnueabihf@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@swc/core-linux-arm64-gnu@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-linux-arm64-gnu@npm:1.4.17" @@ -1222,6 +1348,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-gnu@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-arm64-musl@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-linux-arm64-musl@npm:1.4.17" @@ -1229,6 +1362,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-musl@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@swc/core-linux-x64-gnu@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-linux-x64-gnu@npm:1.4.17" @@ -1236,6 +1376,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-gnu@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-x64-musl@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-linux-x64-musl@npm:1.4.17" @@ -1243,6 +1390,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-musl@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-linux-x64-musl@npm:1.5.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@swc/core-win32-arm64-msvc@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-win32-arm64-msvc@npm:1.4.17" @@ -1250,6 +1404,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-arm64-msvc@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-win32-ia32-msvc@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-win32-ia32-msvc@npm:1.4.17" @@ -1257,6 +1418,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-ia32-msvc@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.3" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@swc/core-win32-x64-msvc@npm:1.4.17": version: 1.4.17 resolution: "@swc/core-win32-x64-msvc@npm:1.4.17" @@ -1264,6 +1432,59 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-x64-msvc@npm:1.5.3": + version: 1.5.3 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/core@npm:^1.2.118": + version: 1.5.3 + resolution: "@swc/core@npm:1.5.3" + dependencies: + "@swc/core-darwin-arm64": "npm:1.5.3" + "@swc/core-darwin-x64": "npm:1.5.3" + "@swc/core-linux-arm-gnueabihf": "npm:1.5.3" + "@swc/core-linux-arm64-gnu": "npm:1.5.3" + "@swc/core-linux-arm64-musl": "npm:1.5.3" + "@swc/core-linux-x64-gnu": "npm:1.5.3" + "@swc/core-linux-x64-musl": "npm:1.5.3" + "@swc/core-win32-arm64-msvc": "npm:1.5.3" + "@swc/core-win32-ia32-msvc": "npm:1.5.3" + "@swc/core-win32-x64-msvc": "npm:1.5.3" + "@swc/counter": "npm:^0.1.2" + "@swc/types": "npm:^0.1.5" + peerDependencies: + "@swc/helpers": ^0.5.0 + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: 10c0/b038251989a600054d5d9cd54ed25febd1c32afa43914c33eeaafd8ed975cbe2491de2187dfacabcd1f6c29aaac55a91d7f9adc3818ef20438e10fd233e91e01 + languageName: node + linkType: hard + "@swc/core@npm:^1.3.107": version: 1.4.17 resolution: "@swc/core@npm:1.4.17" @@ -1326,6 +1547,33 @@ __metadata: languageName: node linkType: hard +"@szmarczak/http-timer@npm:^5.0.1": + version: 5.0.1 + resolution: "@szmarczak/http-timer@npm:5.0.1" + dependencies: + defer-to-connect: "npm:^2.0.1" + checksum: 10c0/4629d2fbb2ea67c2e9dc03af235c0991c79ebdddcbc19aed5d5732fb29ce01c13331e9b1a491584b9069bd6ecde6581dcbf871f11b7eefdebbab34de6cf2197e + languageName: node + linkType: hard + +"@tanstack/query-core@npm:5.35.1": + version: 5.35.1 + resolution: "@tanstack/query-core@npm:5.35.1" + checksum: 10c0/c991efeb29ec42f9aadf43130dac5e4e9e0651880ef96e0cb5f0dc3224b9b919b34f0e4af53231a7de32582aa7ba801ed6683ed7448e41e7b2a344803f08f3fe + languageName: node + linkType: hard + +"@tanstack/react-query@npm:^5.35.1": + version: 5.35.1 + resolution: "@tanstack/react-query@npm:5.35.1" + dependencies: + "@tanstack/query-core": "npm:5.35.1" + peerDependencies: + react: ^18.0.0 + checksum: 10c0/80b3d4e295b05171e34f522c1856bcd561c58963e7661ae2340f3406a3097b7c9896d4f67005876c634ce1d808791498a987b200f00341e6df9fcc862060e6ad + languageName: node + linkType: hard + "@testing-library/dom@npm:^10.0.0": version: 10.1.0 resolution: "@testing-library/dom@npm:10.1.0" @@ -1403,6 +1651,13 @@ __metadata: languageName: node linkType: hard +"@types/http-cache-semantics@npm:^4.0.2": + version: 4.0.4 + resolution: "@types/http-cache-semantics@npm:4.0.4" + checksum: 10c0/51b72568b4b2863e0fe8d6ce8aad72a784b7510d72dc866215642da51d84945a9459fa89f49ec48f1e9a1752e6a78e85a4cda0ded06b1c73e727610c925f9ce6 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -1475,6 +1730,13 @@ __metadata: languageName: node linkType: hard +"@types/yoga-layout@npm:1.9.2": + version: 1.9.2 + resolution: "@types/yoga-layout@npm:1.9.2" + checksum: 10c0/9f2a8618afe3e2e18e76eeaa4ec7d09a85f01f071231f8ff21388d851f940dd7ae5867a5f9aef29eafe44c47453a328d3c718fff1451ab62266450b415e43150 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^7.2.0": version: 7.8.0 resolution: "@typescript-eslint/eslint-plugin@npm:7.8.0" @@ -1687,6 +1949,51 @@ __metadata: languageName: node linkType: hard +"@wry/caches@npm:^1.0.0": + version: 1.0.1 + resolution: "@wry/caches@npm:1.0.1" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/a7bca3377f1131d3f1080f2e39d0692c9d1ca86bfd55734786f167f46aad28a4c8e772107324e8319843fb8068fdf98abcdea376d8a589316b1f0cdadf81f8b1 + languageName: node + linkType: hard + +"@wry/context@npm:^0.7.0": + version: 0.7.4 + resolution: "@wry/context@npm:0.7.4" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/6cc8249b8ba195cda7643bffb30969e33d54a99f118a29dd12f1c34064ee0adf04253cfa0ba5b9893afde0a9588745828962877b9585106f7488e8299757638b + languageName: node + linkType: hard + +"@wry/equality@npm:^0.5.6": + version: 0.5.7 + resolution: "@wry/equality@npm:0.5.7" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/8503ff6d4eb80f303d1387e71e51da59ccfc2160fa6d464618be80946fe43a654ea73f0c5b90d659fc4dfc3e38cbbdd6650d595fe5865be476636e444470853e + languageName: node + linkType: hard + +"@wry/trie@npm:^0.4.3": + version: 0.4.3 + resolution: "@wry/trie@npm:0.4.3" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/1a14edba595b1967d0cf38208c2660b2952a8e8a649bb669b67907df48f602c7f2acbe16c1e1b115afa7d7effb9f1a4dbde38eef16ee92e7521a511262a53281 + languageName: node + linkType: hard + +"@wry/trie@npm:^0.5.0": + version: 0.5.0 + resolution: "@wry/trie@npm:0.5.0" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/8c8cfcac96ba4bc69dabf02740e19e613f501b398e80bacc32cd95e87228f75ecb41cd1a76a65abae9756c0f61ab3536e0da52de28857456f9381ffdf5995d3e + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -1757,7 +2064,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.0": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -1933,6 +2240,13 @@ __metadata: languageName: node linkType: hard +"auto-bind@npm:4.0.0": + version: 4.0.0 + resolution: "auto-bind@npm:4.0.0" + checksum: 10c0/12f70745d081ba990dca028ecfa70de25d4baa9a8b74a5bef3ab293da56cba32ff8276c3ff8e5fe6d9f370547bf3fa71486befbfefe272af7e722c21d0c25530 + languageName: node + linkType: hard + "autoprefixer@npm:^10.4.19": version: 10.4.19 resolution: "autoprefixer@npm:10.4.19" @@ -2095,6 +2409,28 @@ __metadata: languageName: node linkType: hard +"cacheable-lookup@npm:^7.0.0": + version: 7.0.0 + resolution: "cacheable-lookup@npm:7.0.0" + checksum: 10c0/63a9c144c5b45cb5549251e3ea774c04d63063b29e469f7584171d059d3a88f650f47869a974e2d07de62116463d742c287a81a625e791539d987115cb081635 + languageName: node + linkType: hard + +"cacheable-request@npm:^10.2.8": + version: 10.2.14 + resolution: "cacheable-request@npm:10.2.14" + dependencies: + "@types/http-cache-semantics": "npm:^4.0.2" + get-stream: "npm:^6.0.1" + http-cache-semantics: "npm:^4.1.1" + keyv: "npm:^4.5.3" + mimic-response: "npm:^4.0.0" + normalize-url: "npm:^8.0.0" + responselike: "npm:^3.0.0" + checksum: 10c0/41b6658db369f20c03128227ecd219ca7ac52a9d24fc0f499cc9aa5d40c097b48b73553504cebd137024d957c0ddb5b67cf3ac1439b136667f3586257763f88d + languageName: node + linkType: hard + "cachedir@npm:^2.3.0": version: 2.4.0 resolution: "cachedir@npm:2.4.0" @@ -2115,6 +2451,13 @@ __metadata: languageName: node linkType: hard +"call-me-maybe@npm:^1.0.1": + version: 1.0.2 + resolution: "call-me-maybe@npm:1.0.2" + checksum: 10c0/8eff5dbb61141ebb236ed71b4e9549e488bcb5451c48c11e5667d5c75b0532303788a1101e6978cafa2d0c8c1a727805599c2741e3e0982855c9f1d78cd06c9f + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -2136,6 +2479,13 @@ __metadata: languageName: node linkType: hard +"case@npm:^1.6.3": + version: 1.6.3 + resolution: "case@npm:1.6.3" + checksum: 10c0/43fcbb1dff1c4add94dd2bc98bd923d6616f10bff6959adf686d192c3db7d7ced35410761e1ac94cc4a1f5c41c86406ad79d390805539e421e8a77e553f67223 + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -2158,7 +2508,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:5.3.0": +"chalk@npm:5.3.0, chalk@npm:^5.0.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 @@ -2238,6 +2588,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^2.0.0": + version: 2.0.0 + resolution: "ci-info@npm:2.0.0" + checksum: 10c0/8c5fa3830a2bcee2b53c2e5018226f0141db9ec9f7b1e27a5c57db5512332cde8a0beb769bcbaf0d8775a78afbf2bb841928feca4ea6219638a5b088f9884b46 + languageName: node + linkType: hard + "ci-info@npm:^3.2.0": version: 3.9.0 resolution: "ci-info@npm:3.9.0" @@ -2261,6 +2618,13 @@ __metadata: languageName: node linkType: hard +"cli-boxes@npm:^2.2.0": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: 10c0/6111352edbb2f62dbc7bfd58f2d534de507afed7f189f13fa894ce5a48badd94b2aa502fda28f1d7dd5f1eb456e7d4033d09a76660013ef50c7f66e7a034f050 + languageName: node + linkType: hard + "cli-cursor@npm:^3.1.0": version: 3.1.0 resolution: "cli-cursor@npm:3.1.0" @@ -2279,6 +2643,22 @@ __metadata: languageName: node linkType: hard +"cli-highlight@npm:^2.1.11": + version: 2.1.11 + resolution: "cli-highlight@npm:2.1.11" + dependencies: + chalk: "npm:^4.0.0" + highlight.js: "npm:^10.7.1" + mz: "npm:^2.4.0" + parse5: "npm:^5.1.1" + parse5-htmlparser2-tree-adapter: "npm:^6.0.0" + yargs: "npm:^16.0.0" + bin: + highlight: bin/highlight + checksum: 10c0/b5b4af3b968aa9df77eee449a400fbb659cf47c4b03a395370bd98d5554a00afaa5819b41a9a8a1ca0d37b0b896a94e57c65289b37359a25b700b1f56eb04852 + languageName: node + linkType: hard + "cli-table3@npm:~0.6.1": version: 0.6.4 resolution: "cli-table3@npm:0.6.4" @@ -2312,6 +2692,39 @@ __metadata: languageName: node linkType: hard +"clipanion@npm:^3.2.0-rc.10": + version: 3.2.1 + resolution: "clipanion@npm:3.2.1" + dependencies: + typanion: "npm:^3.8.0" + peerDependencies: + typanion: "*" + checksum: 10c0/6c148bd01ae645031aeb6e9a1a16f3ce07eb754cd9981c91edcab82b09e063b805ac41e4f36039d07602334b6dbba036b030d1807c12acd7f90778a696b7ac6e + languageName: node + linkType: hard + +"cliui@npm:^7.0.2": + version: 7.0.4 + resolution: "cliui@npm:7.0.4" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/6035f5daf7383470cef82b3d3db00bec70afb3423538c50394386ffbbab135e26c3689c41791f911fa71b62d13d3863c712fdd70f0fbdffd938a1e6fd09aac00 + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + "clsx@npm:2.0.0": version: 2.0.0 resolution: "clsx@npm:2.0.0" @@ -2326,6 +2739,15 @@ __metadata: languageName: node linkType: hard +"code-excerpt@npm:^3.0.0": + version: 3.0.0 + resolution: "code-excerpt@npm:3.0.0" + dependencies: + convert-to-spaces: "npm:^1.0.1" + checksum: 10c0/5d316ec100cc3ee5e0c4bceb4482fd28d9fc67abaaf8e29a23ad464a6e8fb5a807825704420fb5376482a30672684d707bb0453d844178f10a9855e7b88a70a9 + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -2416,6 +2838,13 @@ __metadata: languageName: node linkType: hard +"convert-to-spaces@npm:^1.0.1": + version: 1.0.2 + resolution: "convert-to-spaces@npm:1.0.2" + checksum: 10c0/cb88c52e05a076ae55856a44b34ffbfc5944e6c21aefa7b3ef0551914674667a2cc9e713eeecc0b507e83f4a521a3876712ddc278ee8653985f6add6917a150b + languageName: node + linkType: hard + "core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -2579,6 +3008,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" + dependencies: + mimic-response: "npm:^3.1.0" + checksum: 10c0/bd89d23141b96d80577e70c54fb226b2f40e74a6817652b80a116d7befb8758261ad073a8895648a29cc0a5947021ab66705cb542fa9c143c82022b27c5b175e + languageName: node + linkType: hard + "deep-eql@npm:^4.1.3": version: 4.1.3 resolution: "deep-eql@npm:4.1.3" @@ -2595,6 +3033,13 @@ __metadata: languageName: node linkType: hard +"defer-to-connect@npm:^2.0.1": + version: 2.0.1 + resolution: "defer-to-connect@npm:2.0.1" + checksum: 10c0/625ce28e1b5ad10cf77057b9a6a727bf84780c17660f6644dab61dd34c23de3001f03cedc401f7d30a4ed9965c2e8a7336e220a329146f2cf85d4eddea429782 + languageName: node + linkType: hard + "define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" @@ -2790,6 +3235,13 @@ __metadata: languageName: node linkType: hard +"es6-promise@npm:^3.2.1": + version: 3.3.1 + resolution: "es6-promise@npm:3.3.1" + checksum: 10c0/b4fc87cb8509c001f62f860f97b05d1fd3f87220c8b832578e6a483c719ca272b73a77f2231cb26395fa865e1cab2fd4298ab67786b69e97b8d757b938f4fc1f + languageName: node + linkType: hard + "esbuild@npm:^0.20.1": version: 0.20.2 resolution: "esbuild@npm:0.20.2" @@ -2884,6 +3336,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -3173,6 +3632,13 @@ __metadata: languageName: node linkType: hard +"fast-safe-stringify@npm:^2.0.7": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: 10c0/d90ec1c963394919828872f21edaa3ad6f1dddd288d2bd4e977027afff09f5db40f94e39536d4646f7e01761d704d72d51dce5af1b93717f3489ef808f5f4e4d + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -3270,6 +3736,13 @@ __metadata: languageName: node linkType: hard +"form-data-encoder@npm:^2.1.2": + version: 2.1.4 + resolution: "form-data-encoder@npm:2.1.4" + checksum: 10c0/4c06ae2b79ad693a59938dc49ebd020ecb58e4584860a90a230f80a68b026483b022ba5e4143cff06ae5ac8fd446a0b500fabc87bbac3d1f62f2757f8dabcaf7 + languageName: node + linkType: hard + "form-data@npm:^4.0.0": version: 4.0.0 resolution: "form-data@npm:4.0.0" @@ -3303,12 +3776,15 @@ __metadata: version: 0.0.0-use.local resolution: "frontend@workspace:." dependencies: + "@openapi-codegen/cli": "npm:^2.0.2" + "@openapi-codegen/typescript": "npm:^8.0.2" "@radix-ui/react-aspect-ratio": "npm:^1.0.3" "@radix-ui/react-dialog": "npm:^1.0.5" "@radix-ui/react-dropdown-menu": "npm:^2.0.6" "@radix-ui/react-label": "npm:^2.0.2" "@radix-ui/react-slot": "npm:^1.0.2" "@radix-ui/react-tabs": "npm:^1.0.4" + "@tanstack/react-query": "npm:^5.35.1" "@testing-library/jest-dom": "npm:^6.4.2" "@testing-library/react": "npm:^15.0.5" "@types/node": "npm:^20.12.7" @@ -3337,6 +3813,7 @@ __metadata: prettier-plugin-tailwindcss: "npm:^0.5.14" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" + react-query-swagger: "npm:^15.11.1" react-router-dom: "npm:^6.23.0" tailwind-merge: "npm:^2.3.0" tailwindcss: "npm:^3.4.3" @@ -3349,9 +3826,20 @@ __metadata: languageName: unknown linkType: soft -"fs-extra@npm:^9.1.0": - version: 9.1.0 - resolution: "fs-extra@npm:9.1.0" +"fs-extra@npm:^10.0.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e + languageName: node + linkType: hard + +"fs-extra@npm:^9.1.0": + version: 9.1.0 + resolution: "fs-extra@npm:9.1.0" dependencies: at-least-node: "npm:^1.0.0" graceful-fs: "npm:^4.2.0" @@ -3412,6 +3900,13 @@ __metadata: languageName: node linkType: hard +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + "get-east-asian-width@npm:^1.0.0": version: 1.2.0 resolution: "get-east-asian-width@npm:1.2.0" @@ -3455,6 +3950,13 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 + languageName: node + linkType: hard + "get-stream@npm:^8.0.1": version: 8.0.1 resolution: "get-stream@npm:8.0.1" @@ -3568,6 +4070,34 @@ __metadata: languageName: node linkType: hard +"got-fetch@npm:^5.1.1": + version: 5.1.9 + resolution: "got-fetch@npm:5.1.9" + peerDependencies: + got: ^12.0.0 + checksum: 10c0/66d7689fe239b25fa6909b76a7f82e85c14f7f4ee4748d1e5d650d97d8b6846f6291bd483ac95f711c9e8c05abc6e8dd73f37eba248bc7fcf59d7171d24cec01 + languageName: node + linkType: hard + +"got@npm:^12.0.0": + version: 12.6.1 + resolution: "got@npm:12.6.1" + dependencies: + "@sindresorhus/is": "npm:^5.2.0" + "@szmarczak/http-timer": "npm:^5.0.1" + cacheable-lookup: "npm:^7.0.0" + cacheable-request: "npm:^10.2.8" + decompress-response: "npm:^6.0.0" + form-data-encoder: "npm:^2.1.2" + get-stream: "npm:^6.0.1" + http2-wrapper: "npm:^2.1.10" + lowercase-keys: "npm:^3.0.0" + p-cancelable: "npm:^3.0.0" + responselike: "npm:^3.0.0" + checksum: 10c0/2fe97fcbd7a9ffc7c2d0ecf59aca0a0562e73a7749cadada9770eeb18efbdca3086262625fb65590594edc220a1eca58fab0d26b0c93c2f9a008234da71ca66b + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -3582,6 +4112,24 @@ __metadata: languageName: node linkType: hard +"graphql-tag@npm:^2.12.6": + version: 2.12.6 + resolution: "graphql-tag@npm:2.12.6" + dependencies: + tslib: "npm:^2.1.0" + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + checksum: 10c0/7763a72011bda454ed8ff1a0d82325f43ca6478e4ce4ab8b7910c4c651dd00db553132171c04d80af5d5aebf1ef6a8a9fd53ccfa33b90ddc00aa3d4be6114419 + languageName: node + linkType: hard + +"graphql@npm:^15.8.0": + version: 15.8.0 + resolution: "graphql@npm:15.8.0" + checksum: 10c0/30cc09b77170a9d1ed68e4c017ec8c5265f69501c96e4f34f8f6613f39a886c96dd9853eac925f212566ed651736334c8fe24ceae6c44e8d7625c95c3009a801 + languageName: node + linkType: hard + "happy-dom@npm:^14.7.1": version: 14.7.1 resolution: "happy-dom@npm:14.7.1" @@ -3639,6 +4187,22 @@ __metadata: languageName: node linkType: hard +"highlight.js@npm:^10.7.1": + version: 10.7.3 + resolution: "highlight.js@npm:10.7.3" + checksum: 10c0/073837eaf816922427a9005c56c42ad8786473dc042332dfe7901aa065e92bc3d94ebf704975257526482066abb2c8677cc0326559bb8621e046c21c5991c434 + languageName: node + linkType: hard + +"hoist-non-react-statics@npm:^3.3.2": + version: 3.3.2 + resolution: "hoist-non-react-statics@npm:3.3.2" + dependencies: + react-is: "npm:^16.7.0" + checksum: 10c0/fe0889169e845d738b59b64badf5e55fa3cf20454f9203d1eb088df322d49d4318df774828e789898dcb280e8a5521bb59b3203385662ca5e9218a6ca5820e74 + languageName: node + linkType: hard + "html-encoding-sniffer@npm:^4.0.0": version: 4.0.0 resolution: "html-encoding-sniffer@npm:4.0.0" @@ -3676,6 +4240,23 @@ __metadata: languageName: node linkType: hard +"http2-client@npm:^1.2.5": + version: 1.3.5 + resolution: "http2-client@npm:1.3.5" + checksum: 10c0/4974f10f5c8b5b7b9e23771190471d02690e9a22c22e028d84715b7ecdcda05017fc9e565476558da3bdf0ba642d24186a94818d0b9afee706ccf9874034be73 + languageName: node + linkType: hard + +"http2-wrapper@npm:^2.1.10": + version: 2.2.1 + resolution: "http2-wrapper@npm:2.2.1" + dependencies: + quick-lru: "npm:^5.1.1" + resolve-alpn: "npm:^1.2.0" + checksum: 10c0/7207201d3c6e53e72e510c9b8912e4f3e468d3ecc0cf3bf52682f2aac9cd99358b896d1da4467380adc151cf97c412bedc59dc13dae90c523f42053a7449eedb + languageName: node + linkType: hard + "https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.4 resolution: "https-proxy-agent@npm:7.0.4" @@ -3780,6 +4361,43 @@ __metadata: languageName: node linkType: hard +"ink@npm:^3.2.0": + version: 3.2.0 + resolution: "ink@npm:3.2.0" + dependencies: + ansi-escapes: "npm:^4.2.1" + auto-bind: "npm:4.0.0" + chalk: "npm:^4.1.0" + cli-boxes: "npm:^2.2.0" + cli-cursor: "npm:^3.1.0" + cli-truncate: "npm:^2.1.0" + code-excerpt: "npm:^3.0.0" + indent-string: "npm:^4.0.0" + is-ci: "npm:^2.0.0" + lodash: "npm:^4.17.20" + patch-console: "npm:^1.0.0" + react-devtools-core: "npm:^4.19.1" + react-reconciler: "npm:^0.26.2" + scheduler: "npm:^0.20.2" + signal-exit: "npm:^3.0.2" + slice-ansi: "npm:^3.0.0" + stack-utils: "npm:^2.0.2" + string-width: "npm:^4.2.2" + type-fest: "npm:^0.12.0" + widest-line: "npm:^3.1.0" + wrap-ansi: "npm:^6.2.0" + ws: "npm:^7.5.5" + yoga-layout-prebuilt: "npm:^1.9.6" + peerDependencies: + "@types/react": ">=16.8.0" + react: ">=16.8.0" + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/dabfd5b73ed1401b18826d0094d7ed4b8055e9fc2043c7c65f98c49a478bc1ffc62fa83ce1503008d1f8ac691930a5b44a10b13ec049d17df21ef121af993b2c + languageName: node + linkType: hard + "invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -3808,6 +4426,17 @@ __metadata: languageName: node linkType: hard +"is-ci@npm:^2.0.0": + version: 2.0.0 + resolution: "is-ci@npm:2.0.0" + dependencies: + ci-info: "npm:^2.0.0" + bin: + is-ci: bin.js + checksum: 10c0/17de4e2cd8f993c56c86472dd53dd9e2c7f126d0ee55afe610557046cdd64de0e8feadbad476edc9eeff63b060523b8673d9094ed2ab294b59efb5a66dd05a9a + languageName: node + linkType: hard + "is-ci@npm:^3.0.1": version: 3.0.1 resolution: "is-ci@npm:3.0.1" @@ -4251,7 +4880,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15, lodash@npm:^4.17.21": +"lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c @@ -4293,7 +4922,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -4313,6 +4942,13 @@ __metadata: languageName: node linkType: hard +"lowercase-keys@npm:^3.0.0": + version: 3.0.0 + resolution: "lowercase-keys@npm:3.0.0" + checksum: 10c0/ef62b9fa5690ab0a6e4ef40c94efce68e3ed124f583cc3be38b26ff871da0178a28b9a84ce0c209653bb25ca135520ab87fea7cd411a54ac4899cb2f30501430 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.2.2 resolution: "lru-cache@npm:10.2.2" @@ -4430,6 +5066,20 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 10c0/0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 + languageName: node + linkType: hard + +"mimic-response@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-response@npm:4.0.0" + checksum: 10c0/761d788d2668ae9292c489605ffd4fad220f442fbae6832adce5ebad086d691e906a6d5240c290293c7a11e99fbdbbef04abbbed498bf8699a4ee0f31315e3fb + languageName: node + linkType: hard + "min-indent@npm:^1.0.0": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -4588,7 +5238,7 @@ __metadata: languageName: node linkType: hard -"mz@npm:^2.7.0": +"mz@npm:^2.4.0, mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" dependencies: @@ -4622,6 +5272,29 @@ __metadata: languageName: node linkType: hard +"node-fetch-h2@npm:^2.3.0": + version: 2.3.0 + resolution: "node-fetch-h2@npm:2.3.0" + dependencies: + http2-client: "npm:^1.2.5" + checksum: 10c0/10f117c5aa1d475fff05028dddd617a61606083e4d6c4195dd5f5b03c973182e0d125e804771e6888d04f7d92b5c9c27a6149d1beedd6af1e0744f163e8a02d9 + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.1": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 10.1.0 resolution: "node-gyp@npm:10.1.0" @@ -4642,6 +5315,15 @@ __metadata: languageName: node linkType: hard +"node-readfiles@npm:^0.2.0": + version: 0.2.0 + resolution: "node-readfiles@npm:0.2.0" + dependencies: + es6-promise: "npm:^3.2.1" + checksum: 10c0/9de2f741baae29f2422b22ef4399b5f7cb6c20372d4e88447a98d00a92cf1a35efdf942d24eee153a87d885aa7e7442b4bc6de33d4b91c47ba9da501780c76a1 + languageName: node + linkType: hard + "node-releases@npm:^2.0.14": version: 2.0.14 resolution: "node-releases@npm:2.0.14" @@ -4674,6 +5356,13 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:^8.0.0": + version: 8.0.1 + resolution: "normalize-url@npm:8.0.1" + checksum: 10c0/eb439231c4b84430f187530e6fdac605c5048ef4ec556447a10c00a91fc69b52d8d8298d9d608e68d3e0f7dc2d812d3455edf425e0f215993667c3183bcab1ef + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.0": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -4692,6 +5381,15 @@ __metadata: languageName: node linkType: hard +"nswag-portable@npm:13.20.0-v.17": + version: 13.20.0-v.17 + resolution: "nswag-portable@npm:13.20.0-v.17" + bin: + nswag-portable: nswag-portable.js + checksum: 10c0/ee140d8e29748796999bfa829b879fc182a3c01141e12407024467f235d9626ecc0d5042b7029ec2fb57493d91dba4ff6730706de1b11170f9d1e3ef7313788a + languageName: node + linkType: hard + "nwsapi@npm:^2.2.7": version: 2.2.9 resolution: "nwsapi@npm:2.2.9" @@ -4699,7 +5397,65 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1": +"oas-kit-common@npm:^1.0.8": + version: 1.0.8 + resolution: "oas-kit-common@npm:1.0.8" + dependencies: + fast-safe-stringify: "npm:^2.0.7" + checksum: 10c0/5619a0bd19a07b52af1afeff26e44601002c0fd558d0020fdb720cb3723b60c83b80efede3a62110ce315f15b971751fb46396760e0e507fb8fd412cdc3808dd + languageName: node + linkType: hard + +"oas-linter@npm:^3.2.2": + version: 3.2.2 + resolution: "oas-linter@npm:3.2.2" + dependencies: + "@exodus/schemasafe": "npm:^1.0.0-rc.2" + should: "npm:^13.2.1" + yaml: "npm:^1.10.0" + checksum: 10c0/5a8ea3d8a0bf185b676659d1e1c0b9b50695aeff53ccd5c264c8e99b4a7c0021f802b937913d76f0bc1a6a2b8ae151df764d95302b0829063b9b26f8c436871c + languageName: node + linkType: hard + +"oas-resolver@npm:^2.5.6": + version: 2.5.6 + resolution: "oas-resolver@npm:2.5.6" + dependencies: + node-fetch-h2: "npm:^2.3.0" + oas-kit-common: "npm:^1.0.8" + reftools: "npm:^1.1.9" + yaml: "npm:^1.10.0" + yargs: "npm:^17.0.1" + bin: + resolve: resolve.js + checksum: 10c0/cfba5ba3f7ea6673a840836cf194a80ba7f77e6d1ee005aa35cc838cad56d7e455fa53753ae7cc38810c96405b8606e675098ea7023639cf546cb10343f180f9 + languageName: node + linkType: hard + +"oas-schema-walker@npm:^1.1.5": + version: 1.1.5 + resolution: "oas-schema-walker@npm:1.1.5" + checksum: 10c0/8ba6bd2a9a8ede2c5574f217653a9e2b889a7c5be69c664a57e293591c58952e8510f4f9e2a82fd5f52491c859ce5c2b68342e9b971e9667f6b811e7fb56fd54 + languageName: node + linkType: hard + +"oas-validator@npm:^5.0.8": + version: 5.0.8 + resolution: "oas-validator@npm:5.0.8" + dependencies: + call-me-maybe: "npm:^1.0.1" + oas-kit-common: "npm:^1.0.8" + oas-linter: "npm:^3.2.2" + oas-resolver: "npm:^2.5.6" + oas-schema-walker: "npm:^1.1.5" + reftools: "npm:^1.1.9" + should: "npm:^13.2.1" + yaml: "npm:^1.10.0" + checksum: 10c0/16bb722042dcba93892c50db2201df6aeea9c3dd60e2f7bc18b36f23c610d136f52a5946908817f6fdd4139219fa4b177f952b9831039078b4c8730fa026b180 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -4747,6 +5503,27 @@ __metadata: languageName: node linkType: hard +"openapi3-ts@npm:^2.0.1": + version: 2.0.2 + resolution: "openapi3-ts@npm:2.0.2" + dependencies: + yaml: "npm:^1.10.2" + checksum: 10c0/2886a85a28d513ef177d069a85293f47e1bb8ba1c9c953f15de064a119f759d6b42b7eb89ed8db0c7d277904fecb65f28a2f4300ebe424f825682b689d2dc810 + languageName: node + linkType: hard + +"optimism@npm:^0.18.0": + version: 0.18.0 + resolution: "optimism@npm:0.18.0" + dependencies: + "@wry/caches": "npm:^1.0.0" + "@wry/context": "npm:^0.7.0" + "@wry/trie": "npm:^0.4.3" + tslib: "npm:^2.3.0" + checksum: 10c0/8e97c6d660cb80cf5f444209b9dd29ee6951fa7b344d4c4fc6d4aaf0ad0710dddaf834d0f5d7211b3658b15ef6c6a22cbcb98c7a8121e3fee9666fe0fd62d876 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -4768,6 +5545,13 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^3.0.0": + version: 3.0.0 + resolution: "p-cancelable@npm:3.0.0" + checksum: 10c0/948fd4f8e87b956d9afc2c6c7392de9113dac817cb1cecf4143f7a3d4c57ab5673614a80be3aba91ceec5e4b69fd8c869852d7e8048bc3d9273c4c36ce14b9aa + languageName: node + linkType: hard + "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -4813,6 +5597,29 @@ __metadata: languageName: node linkType: hard +"parse5-htmlparser2-tree-adapter@npm:^6.0.0": + version: 6.0.1 + resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1" + dependencies: + parse5: "npm:^6.0.1" + checksum: 10c0/dfa5960e2aaf125707e19a4b1bc333de49232eba5a6ffffb95d313a7d6087c3b7a274b58bee8d3bd41bdf150638815d1d601a42bbf2a0345208c3c35b1279556 + languageName: node + linkType: hard + +"parse5@npm:^5.1.1": + version: 5.1.1 + resolution: "parse5@npm:5.1.1" + checksum: 10c0/b0f87a77a7fea5f242e3d76917c983bbea47703b9371801d51536b78942db6441cbda174bf84eb30e47315ddc6f8a0b57d68e562c790154430270acd76c1fa03 + languageName: node + linkType: hard + +"parse5@npm:^6.0.1": + version: 6.0.1 + resolution: "parse5@npm:6.0.1" + checksum: 10c0/595821edc094ecbcfb9ddcb46a3e1fe3a718540f8320eff08b8cf6742a5114cce2d46d45f95c26191c11b184dcaf4e2960abcd9c5ed9eb9393ac9a37efcfdecb + languageName: node + linkType: hard + "parse5@npm:^7.1.2": version: 7.1.2 resolution: "parse5@npm:7.1.2" @@ -4822,6 +5629,13 @@ __metadata: languageName: node linkType: hard +"patch-console@npm:^1.0.0": + version: 1.0.0 + resolution: "patch-console@npm:1.0.0" + checksum: 10c0/32b6d1354d52c3b7add2114a94412b0bd0dd1d0f62300a1f682a75f4b16d0330443bfd98c2c9f06da3a6348273654a230a8b28ff5746497243eca37701d97a50 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -4950,6 +5764,13 @@ __metadata: languageName: node linkType: hard +"pluralize@npm:^8.0.0": + version: 8.0.0 + resolution: "pluralize@npm:8.0.0" + checksum: 10c0/2044cfc34b2e8c88b73379ea4a36fc577db04f651c2909041b054c981cd863dd5373ebd030123ab058d194ae615d3a97cfdac653991e499d10caf592e8b3dc33 + languageName: node + linkType: hard + "postcss-import@npm:^15.1.0": version: 15.1.0 resolution: "postcss-import@npm:15.1.0" @@ -5162,6 +5983,17 @@ __metadata: languageName: node linkType: hard +"prop-types@npm:^15.7.2": + version: 15.8.1 + resolution: "prop-types@npm:15.8.1" + dependencies: + loose-envify: "npm:^1.4.0" + object-assign: "npm:^4.1.1" + react-is: "npm:^16.13.1" + checksum: 10c0/59ece7ca2fb9838031d73a48d4becb9a7cc1ed10e610517c7d8f19a1e02fa47f7c27d557d8a5702bec3cfeccddc853579832b43f449e54635803f277b1c78077 + languageName: node + linkType: hard + "proxy-from-env@npm:1.0.0": version: 1.0.0 resolution: "proxy-from-env@npm:1.0.0" @@ -5216,6 +6048,23 @@ __metadata: languageName: node linkType: hard +"quick-lru@npm:^5.1.1": + version: 5.1.1 + resolution: "quick-lru@npm:5.1.1" + checksum: 10c0/a24cba5da8cec30d70d2484be37622580f64765fb6390a928b17f60cd69e8dbd32a954b3ff9176fa1b86d86ff2ba05252fae55dc4d40d0291c60412b0ad096da + languageName: node + linkType: hard + +"react-devtools-core@npm:^4.19.1": + version: 4.28.5 + resolution: "react-devtools-core@npm:4.28.5" + dependencies: + shell-quote: "npm:^1.6.1" + ws: "npm:^7" + checksum: 10c0/1d71f9b69b8f557a752ba778a20eee9d33bf4393546dd32c96fa034a4b7cc4053f1ac4fccf1ed686a18e1149aa94c26f6d6c3a2c131c958a504199e8503d9ee1 + languageName: node + linkType: hard + "react-dom@npm:^18.2.0": version: 18.3.1 resolution: "react-dom@npm:18.3.1" @@ -5228,6 +6077,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^16.13.1, react-is@npm:^16.7.0": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 + languageName: node + linkType: hard + "react-is@npm:^17.0.1": version: 17.0.2 resolution: "react-is@npm:17.0.2" @@ -5242,6 +6098,36 @@ __metadata: languageName: node linkType: hard +"react-query-swagger@npm:^15.11.1": + version: 15.11.1 + resolution: "react-query-swagger@npm:15.11.1" + dependencies: + nswag-portable: "npm:13.20.0-v.17" + peerDependencies: + "@tanstack/react-query": "> 4.0" + "@tanstack/react-query-persist-client": ">4.2.0" + react: 17.x || 18.x + react-query: ">= 3.33 < 4.0" + typescript: "> 3.0" + bin: + react-query-swagger: cli.js + checksum: 10c0/f40917f7e32204ddbca377bcb9698918e0f19bb3ed231716e56467d8185a8c525b0985b5ed7ac4af56d6dcee55d48fa851e910dba124a33ddb3cd7f9075e0503 + languageName: node + linkType: hard + +"react-reconciler@npm:^0.26.2": + version: 0.26.2 + resolution: "react-reconciler@npm:0.26.2" + dependencies: + loose-envify: "npm:^1.1.0" + object-assign: "npm:^4.1.1" + scheduler: "npm:^0.20.2" + peerDependencies: + react: ^17.0.2 + checksum: 10c0/3ae2e09804d7c1295643b5b3f15bee26cef04e38e0ed1d47c3b3d3d712ca2c37fbc3461ea0d22041b1f3bbf3656c96b8ec3c1df46280f714dcf8e4fe66146bc6 + languageName: node + linkType: hard + "react-remove-scroll-bar@npm:^2.3.3": version: 2.3.6 resolution: "react-remove-scroll-bar@npm:2.3.6" @@ -5355,6 +6241,13 @@ __metadata: languageName: node linkType: hard +"reftools@npm:^1.1.9": + version: 1.1.9 + resolution: "reftools@npm:1.1.9" + checksum: 10c0/4b44c9e75d6e5328b43b974de08776ee1718a0b48f24e033b2699f872cc9a698234a4aa0553b9e1a766b828aeb9834e4aa988410f0279e86179edb33b270da6c + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.14.0": version: 0.14.1 resolution: "regenerator-runtime@npm:0.14.1" @@ -5362,6 +6255,21 @@ __metadata: languageName: node linkType: hard +"rehackt@npm:^0.1.0": + version: 0.1.0 + resolution: "rehackt@npm:0.1.0" + peerDependencies: + "@types/react": "*" + react: "*" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + checksum: 10c0/3d838bfee84ec06c976f21027936f3b0fdb7660ab8a2d4d3f19c65e0daa78a268aa81352311352b8576b89a074714b36ae6cd5bdadb6e975eca079f2b342de73 + languageName: node + linkType: hard + "request-progress@npm:^3.0.0": version: 3.0.0 resolution: "request-progress@npm:3.0.0" @@ -5371,6 +6279,13 @@ __metadata: languageName: node linkType: hard +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -5378,6 +6293,13 @@ __metadata: languageName: node linkType: hard +"resolve-alpn@npm:^1.2.0": + version: 1.2.1 + resolution: "resolve-alpn@npm:1.2.1" + checksum: 10c0/b70b29c1843bc39781ef946c8cd4482e6d425976599c0f9c138cec8209e4e0736161bf39319b01676a847000085dfdaf63583c6fb4427bf751a10635bd2aa0c4 + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -5411,6 +6333,22 @@ __metadata: languageName: node linkType: hard +"response-iterator@npm:^0.2.6": + version: 0.2.6 + resolution: "response-iterator@npm:0.2.6" + checksum: 10c0/60e6b552cd610643269d5d916d270cc8a4bea978cbe4779d6ef8083ac6b89006795508034e4c4ebe204eded75ac32bf243589ba82c1184591dde0674f6db785e + languageName: node + linkType: hard + +"responselike@npm:^3.0.0": + version: 3.0.0 + resolution: "responselike@npm:3.0.0" + dependencies: + lowercase-keys: "npm:^3.0.0" + checksum: 10c0/8af27153f7e47aa2c07a5f2d538cb1e5872995f0e9ff77def858ecce5c3fe677d42b824a62cde502e56d275ab832b0a8bd350d5cd6b467ac0425214ac12ae658 + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -5542,7 +6480,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.5.1": +"rxjs@npm:^7.5.1, rxjs@npm:^7.5.4": version: 7.8.1 resolution: "rxjs@npm:7.8.1" dependencies: @@ -5574,6 +6512,16 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.20.2": + version: 0.20.2 + resolution: "scheduler@npm:0.20.2" + dependencies: + loose-envify: "npm:^1.1.0" + object-assign: "npm:^4.1.1" + checksum: 10c0/b0982e4b0f34f4ffa4f2f486161c0fd9ce9b88680b045dccbf250eb1aa4fd27413570645455187a83535e2370f5c667a251045547765408492bd883cbe95fcdb + languageName: node + linkType: hard + "scheduler@npm:^0.23.2": version: 0.23.2 resolution: "scheduler@npm:0.23.2" @@ -5624,6 +6572,69 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.6.1": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a + languageName: node + linkType: hard + +"should-equal@npm:^2.0.0": + version: 2.0.0 + resolution: "should-equal@npm:2.0.0" + dependencies: + should-type: "npm:^1.4.0" + checksum: 10c0/b375e1da2586671e2b9442ac5b700af508f56438af9923f69123b1fe4e02ccddc9a8a3eb803447a6df91e616cec236c41d6f28fdaa100467f9fdb81651089538 + languageName: node + linkType: hard + +"should-format@npm:^3.0.3": + version: 3.0.3 + resolution: "should-format@npm:3.0.3" + dependencies: + should-type: "npm:^1.3.0" + should-type-adaptors: "npm:^1.0.1" + checksum: 10c0/ef2a31148d79a3fabd0dc6c1c1b10f90d9e071ad8e1f99452bd01e8aceaca62985b43974cf8103185fa1a3ade85947c6f664e44ca9af253afd1ce93c223bd8e4 + languageName: node + linkType: hard + +"should-type-adaptors@npm:^1.0.1": + version: 1.1.0 + resolution: "should-type-adaptors@npm:1.1.0" + dependencies: + should-type: "npm:^1.3.0" + should-util: "npm:^1.0.0" + checksum: 10c0/cf127f8807f69ace9db04dbec3f274330a854405feef9821b5fa525748961da65747869cca36c813132b98757bd3e42d53541579cb16630ccf3c0dd9c0082320 + languageName: node + linkType: hard + +"should-type@npm:^1.3.0, should-type@npm:^1.4.0": + version: 1.4.0 + resolution: "should-type@npm:1.4.0" + checksum: 10c0/50cb50d776ee117b151068367c09ec12ac8e6f5fe2bd4d167413972813f06e930fe8624232a56c335846d3afcb784455f9a9690baa4350b3919bd001f0c4c94b + languageName: node + linkType: hard + +"should-util@npm:^1.0.0": + version: 1.0.1 + resolution: "should-util@npm:1.0.1" + checksum: 10c0/1790719e05eae9edae86e44cbbad98529bd333df3f7cdfd63ea80acb6af718990e70abbc173aa9ccb93fff5ab6ee08d38412d707ff4003840be2256a278a61f3 + languageName: node + linkType: hard + +"should@npm:^13.2.1": + version: 13.2.3 + resolution: "should@npm:13.2.3" + dependencies: + should-equal: "npm:^2.0.0" + should-format: "npm:^3.0.3" + should-type: "npm:^1.4.0" + should-type-adaptors: "npm:^1.0.1" + should-util: "npm:^1.0.0" + checksum: 10c0/99581d8615f6fb27cd23c9f431cfacef58d118a90d0cccf58775b90631a47441397cfbdcbe6379e2718e9e60f293e3dfc0e87857f4b5a36fe962814e46ab05fa + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.6 resolution: "side-channel@npm:1.0.6" @@ -5675,6 +6686,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^4.0.0": + version: 4.0.0 + resolution: "slash@npm:4.0.0" + checksum: 10c0/b522ca75d80d107fd30d29df0549a7b2537c83c4c4ecd12cd7d4ea6c8aaca2ab17ada002e7a1d78a9d736a0261509f26ea5b489082ee443a3a810586ef8eff18 + languageName: node + linkType: hard + "slice-ansi@npm:^3.0.0": version: 3.0.0 resolution: "slice-ansi@npm:3.0.0" @@ -5789,6 +6807,15 @@ __metadata: languageName: node linkType: hard +"stack-utils@npm:^2.0.2": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" + dependencies: + escape-string-regexp: "npm:^2.0.0" + checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a + languageName: node + linkType: hard + "stackback@npm:0.0.2": version: 0.0.2 resolution: "stackback@npm:0.0.2" @@ -5810,7 +6837,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -5952,6 +6979,36 @@ __metadata: languageName: node linkType: hard +"swagger2openapi@npm:^7.0.8": + version: 7.0.8 + resolution: "swagger2openapi@npm:7.0.8" + dependencies: + call-me-maybe: "npm:^1.0.1" + node-fetch: "npm:^2.6.1" + node-fetch-h2: "npm:^2.3.0" + node-readfiles: "npm:^0.2.0" + oas-kit-common: "npm:^1.0.8" + oas-resolver: "npm:^2.5.6" + oas-schema-walker: "npm:^1.1.5" + oas-validator: "npm:^5.0.8" + reftools: "npm:^1.1.9" + yaml: "npm:^1.10.0" + yargs: "npm:^17.0.1" + bin: + boast: boast.js + oas-validate: oas-validate.js + swagger2openapi: swagger2openapi.js + checksum: 10c0/441a4d3a7d353f99395b14a0c8d6124be6390f2f8aa53336905e7314a7f80b66f5f2a40ac0dc2dbe2f7bc01f52a223a94f54a2ece345095fd3ad8ae8b03d688b + languageName: node + linkType: hard + +"symbol-observable@npm:^4.0.0": + version: 4.0.0 + resolution: "symbol-observable@npm:4.0.0" + checksum: 10c0/5e9a3ab08263a6be8cbee76587ad5880dcc62a47002787ed5ebea56b1eb30dc87da6f0183d67e88286806799fbe21c69077fbd677be4be2188e92318d6c6f31d + languageName: node + linkType: hard + "symbol-tree@npm:^3.2.4": version: 3.2.4 resolution: "symbol-tree@npm:3.2.4" @@ -6128,6 +7185,13 @@ __metadata: languageName: node linkType: hard +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + "ts-api-utils@npm:^1.3.0": version: 1.3.0 resolution: "ts-api-utils@npm:1.3.0" @@ -6144,13 +7208,40 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0": +"ts-invariant@npm:^0.10.3": + version: 0.10.3 + resolution: "ts-invariant@npm:0.10.3" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/2fbc178d5903d325ee0b87fad38827eac11888b6e86979b06754fd4bcdcf44c2a99b8bcd5d59d149c0464ede55ae810b02a2aee6835ad10efe4dd0e22efd68c0 + languageName: node + linkType: hard + +"tslib@npm:^1.8.1": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 + languageName: node + linkType: hard + +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb languageName: node linkType: hard +"tsutils@npm:^3.21.0": + version: 3.21.0 + resolution: "tsutils@npm:3.21.0" + dependencies: + tslib: "npm:^1.8.1" + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + checksum: 10c0/02f19e458ec78ead8fffbf711f834ad8ecd2cc6ade4ec0320790713dccc0a412b99e7fd907c4cda2a1dc602c75db6f12e0108e87a5afad4b2f9e90a24cabd5a2 + languageName: node + linkType: hard + "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -6167,6 +7258,13 @@ __metadata: languageName: node linkType: hard +"typanion@npm:^3.7.1, typanion@npm:^3.8.0": + version: 3.14.0 + resolution: "typanion@npm:3.14.0" + checksum: 10c0/8b03b19844e6955bfd906c31dc781bae6d7f1fb3ce4fe24b7501557013d4889ae5cefe671dafe98d87ead0adceb8afcb8bc16df7dc0bd2b7331bac96f3a7cae2 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -6183,6 +7281,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^0.12.0": + version: 0.12.0 + resolution: "type-fest@npm:0.12.0" + checksum: 10c0/7f88f99fe4aaf2c2e2b0a601c63164e3b218b9378c9bc5d8b514c5170eabd4732abd3f74bb97323c387ae340021d1d814369ef52ab8057481cb785e5306f23ac + languageName: node + linkType: hard + "type-fest@npm:^0.20.2": version: 0.20.2 resolution: "type-fest@npm:0.20.2" @@ -6197,6 +7302,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:4.8.2": + version: 4.8.2 + resolution: "typescript@npm:4.8.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/562cae44b09b04b280822b4cb34e7c50543b5f871297219a736a4f2cadb80ba493b21e3890bec6fee6b25a0507fb529eca40ebc690ac9020a5135af838746e72 + languageName: node + linkType: hard + "typescript@npm:^5.2.2": version: 5.4.5 resolution: "typescript@npm:5.4.5" @@ -6207,6 +7322,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A4.8.2#optional!builtin": + version: 4.8.2 + resolution: "typescript@patch:typescript@npm%3A4.8.2#optional!builtin::version=4.8.2&hash=3b564f" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/8e7760580c82eba5f2dc91fa5a2f59d8385a9a1b48228ac4921abfa5496beccb2a11ed68a78699accc3ec12dd8a520a8407a222904206c6cf2de478a7e714ff3 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A^5.2.2#optional!builtin": version: 5.4.5 resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c" @@ -6484,6 +7609,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + "webidl-conversions@npm:^7.0.0": version: 7.0.0 resolution: "webidl-conversions@npm:7.0.0" @@ -6524,6 +7656,16 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -6558,6 +7700,15 @@ __metadata: languageName: node linkType: hard +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: "npm:^4.0.0" + checksum: 10c0/b1e623adcfb9df35350dd7fc61295d6d4a1eaa65a406ba39c4b8360045b614af95ad10e05abf704936ed022569be438c4bfa02d6d031863c4166a238c301119f + languageName: node + linkType: hard + "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" @@ -6616,6 +7767,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^7, ws@npm:^7.5.5": + version: 7.5.9 + resolution: "ws@npm:7.5.9" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/aec4ef4eb65821a7dde7b44790f8699cfafb7978c9b080f6d7a98a7f8fc0ce674c027073a78574c94786ba7112cc90fa2cc94fc224ceba4d4b1030cff9662494 + languageName: node + linkType: hard + "ws@npm:^8.16.0": version: 8.17.0 resolution: "ws@npm:8.17.0" @@ -6645,6 +7811,13 @@ __metadata: languageName: node linkType: hard +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -6659,6 +7832,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^1.10.0, yaml@npm:^1.10.2": + version: 1.10.2 + resolution: "yaml@npm:1.10.2" + checksum: 10c0/5c28b9eb7adc46544f28d9a8d20c5b3cb1215a886609a2fd41f51628d8aaa5878ccd628b755dbcd29f6bb4921bd04ffbc6dcc370689bb96e594e2f9813d2605f + languageName: node + linkType: hard + "yaml@npm:^2.3.4": version: 2.4.2 resolution: "yaml@npm:2.4.2" @@ -6668,6 +7848,50 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^20.2.2": + version: 20.2.9 + resolution: "yargs-parser@npm:20.2.9" + checksum: 10c0/0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^16.0.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" + dependencies: + cliui: "npm:^7.0.2" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + languageName: node + linkType: hard + +"yargs@npm:^17.0.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + "yauzl@npm:^2.10.0": version: 2.10.0 resolution: "yauzl@npm:2.10.0" @@ -6692,6 +7916,31 @@ __metadata: languageName: node linkType: hard +"yoga-layout-prebuilt@npm:^1.9.6": + version: 1.10.0 + resolution: "yoga-layout-prebuilt@npm:1.10.0" + dependencies: + "@types/yoga-layout": "npm:1.9.2" + checksum: 10c0/e83b6b7078faf4d0472461b53e92bf9cae655de3d896aee5f79b5ba5a960e507bbf8e671b261db13137bf18711686969f19fd1d9c4669beb1d70754b83c5879d + languageName: node + linkType: hard + +"zen-observable-ts@npm:^1.2.5": + version: 1.2.5 + resolution: "zen-observable-ts@npm:1.2.5" + dependencies: + zen-observable: "npm:0.8.15" + checksum: 10c0/21d586f3d0543e1d6f05d9333a137b407dbf337907c1ee1c2fa7a7da044f7e1262e4baf4ef8902f230c6f5acb561047659eb7df73df33307233cc451efe46db1 + languageName: node + linkType: hard + +"zen-observable@npm:0.8.15": + version: 0.8.15 + resolution: "zen-observable@npm:0.8.15" + checksum: 10c0/71cc2f2bbb537300c3f569e25693d37b3bc91f225cefce251a71c30bc6bb3e7f8e9420ca0eb57f2ac9e492b085b8dfa075fd1e8195c40b83c951dd59c6e4fbf8 + languageName: node + linkType: hard + "zod@npm:^3.23.5": version: 3.23.5 resolution: "zod@npm:3.23.5" diff --git a/swagger/openapi.yml b/swagger/openapi.yml index e8c28e01..fa0d9b9e 100644 --- a/swagger/openapi.yml +++ b/swagger/openapi.yml @@ -30,6 +30,7 @@ tags: paths: /users/logout: post: + operationId: logout tags: - auth summary: User logout @@ -42,14 +43,17 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: object + "400": + $ref: "#/components/responses/BadRequestResponse" /users/verify-email: post: + operationId: verifyEmail tags: - auth summary: Verify user's email @@ -71,7 +75,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -81,6 +85,7 @@ paths: /users/resend-verification-email: post: + operationId: resendVerificationEmail tags: - auth summary: Resend verification email @@ -93,7 +98,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -103,6 +108,7 @@ paths: /users/reset-password-request: post: + operationId: resetPasswordRequest tags: - auth summary: Request password reset @@ -128,7 +134,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -138,6 +144,7 @@ paths: /users/reset-password: post: + operationId: resetPassword tags: - auth summary: Reset password @@ -162,7 +169,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -172,6 +179,7 @@ paths: /users/signup: post: + operationId: signup tags: - auth summary: Register a new user @@ -189,6 +197,7 @@ paths: /users/login: post: + operationId: login tags: - auth summary: User login @@ -205,7 +214,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -215,6 +224,7 @@ paths: /users/{userId}: get: + operationId: getUserById tags: - user summary: Get user profile @@ -233,7 +243,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -242,6 +252,7 @@ paths: $ref: "#/components/responses/NotFoundResponse" put: + operationId: updateUserById tags: - user summary: Update user profile @@ -266,7 +277,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -276,6 +287,7 @@ paths: /users/{userId}/following: get: + operationId: getUserFollowing tags: - user summary: Get users being followed @@ -294,16 +306,19 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: array items: $ref: "#/components/schemas/UserSummary" + "400": + $ref: "#/components/responses/BadRequestResponse" /users/{userId}/followers: get: + operationId: getUserFollowers tags: - user summary: Get user's followers @@ -320,16 +335,19 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: array items: $ref: "#/components/schemas/UserSummary" + "400": + $ref: "#/components/responses/BadRequestResponse" /users/follow: post: + operationId: followUser tags: - user summary: Follow a user @@ -354,6 +372,7 @@ paths: /users/unfollow: post: + operationId: unfollowUser tags: - user summary: Unfollow a user @@ -378,6 +397,7 @@ paths: /search/users: get: + operationId: searchUsers tags: - user summary: Search for users @@ -393,16 +413,19 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: array items: $ref: "#/components/schemas/UserSummary" + "400": + $ref: "#/components/responses/BadRequestResponse" /search/dishes: get: + operationId: searchDishes tags: - dish summary: Search for dishes @@ -428,16 +451,19 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: array items: - $ref: "#/components/schemas/DishSearchResult" + $ref: "#/components/schemas/DishDetails" + "400": + $ref: "#/components/responses/BadRequestResponse" /dishes/{dishId}: get: + operationId: getDishById tags: - dish summary: Get dish details @@ -454,7 +480,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -464,6 +490,7 @@ paths: /cuisines/{cuisineId}: get: + operationId: getCuisineById tags: - cuisines summary: Get cuisine details @@ -484,7 +511,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -494,6 +521,7 @@ paths: /recipes: get: + operationId: getRecipesForEntity tags: - recipe summary: Get recipes for dish, cuisine @@ -518,15 +546,18 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: type: array items: $ref: "#/components/schemas/RecipeSummary" + "400": + $ref: "#/components/responses/BadRequestResponse" post: + operationId: createRecipe tags: - recipe summary: Create a new recipe @@ -545,7 +576,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/CreatedResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -555,6 +586,7 @@ paths: /recipes/{recipeId}: get: + operationId: getRecipeById tags: - recipe summary: Get recipe details @@ -571,7 +603,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -580,6 +612,7 @@ paths: $ref: "#/components/responses/NotFoundResponse" delete: + operationId: deleteRecipeById tags: - recipe summary: Delete a recipe @@ -593,7 +626,8 @@ paths: type: integer responses: "204": - description: No content + description: OK + $ref: "#/components/responses/OkResponse" "403": $ref: "#/components/responses/ForbiddenResponse" "404": @@ -601,6 +635,7 @@ paths: /recipes/{recipeId}/rating: post: + operationId: rateRecipe tags: - recipe summary: Rate a recipe @@ -631,6 +666,7 @@ paths: /recipes/{recipeId}/bookmark: post: + operationId: bookmarkRecipe tags: - recipe summary: Bookmark a recipe @@ -650,6 +686,7 @@ paths: /feed: get: + operationId: getFeed tags: - user summary: Get user's feed @@ -669,7 +706,7 @@ paths: application/json: schema: allOf: - - $ref: "#/components/responses/OkResponse/content/application%2Fjson/schema" + - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: @@ -817,9 +854,8 @@ components: recipeCount: 10 avgRating: 4.5 - DishSearchResult: + DishDetails: type: object - $ref: "#/components/schemas/DishDetails" examples: - id: "http://www.wikidata.org/entity/Q905527" name: "takoyaki" @@ -829,13 +865,12 @@ components: ingredients: "wheat flour, green laver, octopus as food, beni shōga, katsuobushi, Welsh onion" foodTypes: "konamono, octopus dish, yakimono" cuisines: "Japanese cuisine" - - DishDetails: - type: object required: - id - name - image + - description + - countries properties: id: type: string @@ -1082,6 +1117,7 @@ components: CuisineSummary: type: object + required: ["id", "name"] properties: id: type: integer @@ -1093,6 +1129,7 @@ components: DishSummary: type: object + required: ["id", "name"] properties: id: type: integer @@ -1103,19 +1140,9 @@ components: name: "takoyaki" ApiResponse: - type: object - required: - - status - properties: - status: - type: integer - description: <300 indicates success, >=400 indicates error. - data: - type: object - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + oneOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - $ref: "#/components/schemas/ErrorResponseObject" examples: - status: 200 data: { "message": "Success" } @@ -1136,112 +1163,126 @@ components: field: type: string description: If empty, indicates an error not related to any field. + SuccessResponseObject: + description: OK + type: object + required: ["status", "data"] + properties: + status: + type: integer + description: + Internal status code of the response. An HTTP 200 response with an internal + 500 status code is an error response. Prioritive the inner status over the HTTP + status. + enum: + - 200 + - 201 + examples: [200, 201] + data: + oneOf: + - type: object + - type: array + ErrorResponseObject: + description: Response with errors + type: object + required: ["status", "errors"] + properties: + status: + enum: + - 400 + - 401 + - 403 + - 404 + - 409 + - 500 + description: + Internal status code of the response. An HTTP 200 response with an internal + 500 status code is an error response. Prioritive the inner status over the HTTP + status. + examples: [400, 401, 403, 404, 409, 500] + errors: + type: array + items: + $ref: "#/components/schemas/ApiError" responses: OkResponse: description: OK content: application/json: schema: - type: object - required: ["status"] - properties: - status: - type: integer - examples: [200] - data: - type: object + $ref: "#/components/schemas/SuccessResponseObject" CreatedResponse: description: Created content: application/json: schema: - type: object - properties: - status: - type: integer - examples: [201] - data: - type: object + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + status: + const: 201 BadRequestResponse: description: Bad Request content: application/json: schema: - type: object - properties: - status: - type: integer - examples: [400] - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + allOf: + - $ref: "#/components/schemas/ErrorResponseObject" + - type: object + properties: + status: + const: 400 UnauthorizedResponse: description: Unauthorized content: application/json: schema: - type: object - properties: - status: - type: integer - examples: - - 401 - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + allOf: + - $ref: "#/components/schemas/ErrorResponseObject" + - type: object + properties: + status: + const: 401 ForbiddenResponse: description: Forbidden content: application/json: schema: - type: object - properties: - status: - type: integer - examples: - - 403 - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + allOf: + - $ref: "#/components/schemas/ErrorResponseObject" + - type: object + properties: + status: + const: 403 NotFoundResponse: description: Not Found content: application/json: schema: - type: object - properties: - status: - type: integer - examples: - - 404 - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + allOf: + - $ref: "#/components/schemas/ErrorResponseObject" + - type: object + properties: + status: + const: 404 ConflictResponse: description: Conflict content: application/json: schema: - type: object - properties: - status: - type: integer - examples: - - 409 - errors: - type: array - items: - $ref: "#/components/schemas/ApiError" + allOf: + - $ref: "#/components/schemas/ErrorResponseObject" + - type: object + properties: + status: + const: 409 InternalServerErrorResponse: description: Internal Server Error From 4542898e6b267e20f0045ea34b3121d373d125b1 Mon Sep 17 00:00:00 2001 From: Nazire Date: Tue, 7 May 2024 17:34:31 +0300 Subject: [PATCH 11/74] Added Icons under assets --- frontend/src/assets/Icon/Food/BakedGood.svg | 5 +++++ frontend/src/assets/Icon/Food/Bread.svg | 5 +++++ frontend/src/assets/Icon/Food/ChickenDish.svg | 8 ++++++++ frontend/src/assets/Icon/Food/Cookie.svg | 9 +++++++++ frontend/src/assets/Icon/Food/Dessert.svg | 6 ++++++ frontend/src/assets/Icon/Food/Dip.svg | 14 ++++++++++++++ frontend/src/assets/Icon/Food/Drink.svg | 4 ++++ frontend/src/assets/Icon/Food/Egg.svg | 5 +++++ frontend/src/assets/Icon/Food/FruitDish.svg | 6 ++++++ frontend/src/assets/Icon/Food/HalalFood.svg | 4 ++++ frontend/src/assets/Icon/Food/Hamburger.svg | 5 +++++ frontend/src/assets/Icon/Food/KosherFood.svg | 4 ++++ frontend/src/assets/Icon/Food/MeatDish.svg | 6 ++++++ frontend/src/assets/Icon/Food/NoodleDish.svg | 5 +++++ frontend/src/assets/Icon/Food/PastaDish.svg | 12 ++++++++++++ frontend/src/assets/Icon/Food/Pizza.svg | 7 +++++++ frontend/src/assets/Icon/Food/PorkDish.svg | 10 ++++++++++ frontend/src/assets/Icon/Food/RiceDish.svg | 11 +++++++++++ frontend/src/assets/Icon/Food/Roll.svg | 5 +++++ frontend/src/assets/Icon/Food/Salad.svg | 6 ++++++ frontend/src/assets/Icon/Food/Sandwich.svg | 3 +++ frontend/src/assets/Icon/Food/Seafood.svg | 6 ++++++ frontend/src/assets/Icon/Food/Skewer.svg | 3 +++ frontend/src/assets/Icon/Food/SoftDrink.svg | 9 +++++++++ frontend/src/assets/Icon/Food/Soup.svg | 3 +++ frontend/src/assets/Icon/Food/Sushi.svg | 8 ++++++++ frontend/src/assets/Icon/Food/VeganFood.svg | 3 +++ .../src/assets/Icon/Food/VegetarianCuisine.svg | 3 +++ frontend/src/assets/Icon/General/Arrow-Right.svg | 5 +++++ frontend/src/assets/Icon/General/Edit.svg | 5 +++++ frontend/src/assets/Icon/General/Minus-Border.svg | 5 +++++ frontend/src/assets/Icon/General/Plus-Border.svg | 5 +++++ frontend/src/assets/Icon/General/Plus.svg | 5 +++++ frontend/src/assets/Icon/General/Profile.svg | 7 +++++++ frontend/src/assets/Icon/General/Recipe.png | Bin 0 -> 614 bytes frontend/src/assets/Icon/Nav/Bookmark/Active.svg | 8 ++++++++ .../src/assets/Icon/Nav/Bookmark/Inactive.svg | 5 +++++ frontend/src/assets/Icon/Nav/Home/Active.svg | 8 ++++++++ frontend/src/assets/Icon/Nav/Home/Inactive.svg | 7 +++++++ .../src/assets/Icon/Nav/Notification/Active.svg | 8 ++++++++ .../src/assets/Icon/Nav/Notification/Inactive.svg | 5 +++++ frontend/src/assets/Icon/Nav/Profile/Active.svg | 8 ++++++++ frontend/src/assets/Icon/Nav/Profile/Inactive.svg | 5 +++++ yarn.lock | 4 ++++ 44 files changed, 265 insertions(+) create mode 100644 frontend/src/assets/Icon/Food/BakedGood.svg create mode 100644 frontend/src/assets/Icon/Food/Bread.svg create mode 100644 frontend/src/assets/Icon/Food/ChickenDish.svg create mode 100644 frontend/src/assets/Icon/Food/Cookie.svg create mode 100644 frontend/src/assets/Icon/Food/Dessert.svg create mode 100644 frontend/src/assets/Icon/Food/Dip.svg create mode 100644 frontend/src/assets/Icon/Food/Drink.svg create mode 100644 frontend/src/assets/Icon/Food/Egg.svg create mode 100644 frontend/src/assets/Icon/Food/FruitDish.svg create mode 100644 frontend/src/assets/Icon/Food/HalalFood.svg create mode 100644 frontend/src/assets/Icon/Food/Hamburger.svg create mode 100644 frontend/src/assets/Icon/Food/KosherFood.svg create mode 100644 frontend/src/assets/Icon/Food/MeatDish.svg create mode 100644 frontend/src/assets/Icon/Food/NoodleDish.svg create mode 100644 frontend/src/assets/Icon/Food/PastaDish.svg create mode 100644 frontend/src/assets/Icon/Food/Pizza.svg create mode 100644 frontend/src/assets/Icon/Food/PorkDish.svg create mode 100644 frontend/src/assets/Icon/Food/RiceDish.svg create mode 100644 frontend/src/assets/Icon/Food/Roll.svg create mode 100644 frontend/src/assets/Icon/Food/Salad.svg create mode 100644 frontend/src/assets/Icon/Food/Sandwich.svg create mode 100644 frontend/src/assets/Icon/Food/Seafood.svg create mode 100644 frontend/src/assets/Icon/Food/Skewer.svg create mode 100644 frontend/src/assets/Icon/Food/SoftDrink.svg create mode 100644 frontend/src/assets/Icon/Food/Soup.svg create mode 100644 frontend/src/assets/Icon/Food/Sushi.svg create mode 100644 frontend/src/assets/Icon/Food/VeganFood.svg create mode 100644 frontend/src/assets/Icon/Food/VegetarianCuisine.svg create mode 100644 frontend/src/assets/Icon/General/Arrow-Right.svg create mode 100644 frontend/src/assets/Icon/General/Edit.svg create mode 100644 frontend/src/assets/Icon/General/Minus-Border.svg create mode 100644 frontend/src/assets/Icon/General/Plus-Border.svg create mode 100644 frontend/src/assets/Icon/General/Plus.svg create mode 100644 frontend/src/assets/Icon/General/Profile.svg create mode 100644 frontend/src/assets/Icon/General/Recipe.png create mode 100644 frontend/src/assets/Icon/Nav/Bookmark/Active.svg create mode 100644 frontend/src/assets/Icon/Nav/Bookmark/Inactive.svg create mode 100644 frontend/src/assets/Icon/Nav/Home/Active.svg create mode 100644 frontend/src/assets/Icon/Nav/Home/Inactive.svg create mode 100644 frontend/src/assets/Icon/Nav/Notification/Active.svg create mode 100644 frontend/src/assets/Icon/Nav/Notification/Inactive.svg create mode 100644 frontend/src/assets/Icon/Nav/Profile/Active.svg create mode 100644 frontend/src/assets/Icon/Nav/Profile/Inactive.svg create mode 100644 yarn.lock diff --git a/frontend/src/assets/Icon/Food/BakedGood.svg b/frontend/src/assets/Icon/Food/BakedGood.svg new file mode 100644 index 00000000..212c26c7 --- /dev/null +++ b/frontend/src/assets/Icon/Food/BakedGood.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/Bread.svg b/frontend/src/assets/Icon/Food/Bread.svg new file mode 100644 index 00000000..f323a37e --- /dev/null +++ b/frontend/src/assets/Icon/Food/Bread.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/ChickenDish.svg b/frontend/src/assets/Icon/Food/ChickenDish.svg new file mode 100644 index 00000000..83c5efe9 --- /dev/null +++ b/frontend/src/assets/Icon/Food/ChickenDish.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Cookie.svg b/frontend/src/assets/Icon/Food/Cookie.svg new file mode 100644 index 00000000..4c25b75b --- /dev/null +++ b/frontend/src/assets/Icon/Food/Cookie.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Dessert.svg b/frontend/src/assets/Icon/Food/Dessert.svg new file mode 100644 index 00000000..e6e63769 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Dessert.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/Food/Dip.svg b/frontend/src/assets/Icon/Food/Dip.svg new file mode 100644 index 00000000..1c7ac663 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Dip.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Drink.svg b/frontend/src/assets/Icon/Food/Drink.svg new file mode 100644 index 00000000..270a43f5 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Drink.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/Icon/Food/Egg.svg b/frontend/src/assets/Icon/Food/Egg.svg new file mode 100644 index 00000000..f4042165 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Egg.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/FruitDish.svg b/frontend/src/assets/Icon/Food/FruitDish.svg new file mode 100644 index 00000000..e633f112 --- /dev/null +++ b/frontend/src/assets/Icon/Food/FruitDish.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/Food/HalalFood.svg b/frontend/src/assets/Icon/Food/HalalFood.svg new file mode 100644 index 00000000..42c1e819 --- /dev/null +++ b/frontend/src/assets/Icon/Food/HalalFood.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/Icon/Food/Hamburger.svg b/frontend/src/assets/Icon/Food/Hamburger.svg new file mode 100644 index 00000000..82eec376 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Hamburger.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/KosherFood.svg b/frontend/src/assets/Icon/Food/KosherFood.svg new file mode 100644 index 00000000..cd2c2eef --- /dev/null +++ b/frontend/src/assets/Icon/Food/KosherFood.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/Icon/Food/MeatDish.svg b/frontend/src/assets/Icon/Food/MeatDish.svg new file mode 100644 index 00000000..cf7f0732 --- /dev/null +++ b/frontend/src/assets/Icon/Food/MeatDish.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/Food/NoodleDish.svg b/frontend/src/assets/Icon/Food/NoodleDish.svg new file mode 100644 index 00000000..f1ab43fe --- /dev/null +++ b/frontend/src/assets/Icon/Food/NoodleDish.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/PastaDish.svg b/frontend/src/assets/Icon/Food/PastaDish.svg new file mode 100644 index 00000000..20cd9c9f --- /dev/null +++ b/frontend/src/assets/Icon/Food/PastaDish.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Pizza.svg b/frontend/src/assets/Icon/Food/Pizza.svg new file mode 100644 index 00000000..bf5e4f03 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Pizza.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/Icon/Food/PorkDish.svg b/frontend/src/assets/Icon/Food/PorkDish.svg new file mode 100644 index 00000000..015f113b --- /dev/null +++ b/frontend/src/assets/Icon/Food/PorkDish.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/RiceDish.svg b/frontend/src/assets/Icon/Food/RiceDish.svg new file mode 100644 index 00000000..530a9a7c --- /dev/null +++ b/frontend/src/assets/Icon/Food/RiceDish.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Roll.svg b/frontend/src/assets/Icon/Food/Roll.svg new file mode 100644 index 00000000..9853b809 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Roll.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Food/Salad.svg b/frontend/src/assets/Icon/Food/Salad.svg new file mode 100644 index 00000000..f0970929 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Salad.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/Food/Sandwich.svg b/frontend/src/assets/Icon/Food/Sandwich.svg new file mode 100644 index 00000000..7e017800 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Sandwich.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/Food/Seafood.svg b/frontend/src/assets/Icon/Food/Seafood.svg new file mode 100644 index 00000000..24be3d0f --- /dev/null +++ b/frontend/src/assets/Icon/Food/Seafood.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/Food/Skewer.svg b/frontend/src/assets/Icon/Food/Skewer.svg new file mode 100644 index 00000000..45b2cedd --- /dev/null +++ b/frontend/src/assets/Icon/Food/Skewer.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/Food/SoftDrink.svg b/frontend/src/assets/Icon/Food/SoftDrink.svg new file mode 100644 index 00000000..735069bc --- /dev/null +++ b/frontend/src/assets/Icon/Food/SoftDrink.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/Soup.svg b/frontend/src/assets/Icon/Food/Soup.svg new file mode 100644 index 00000000..c5db7d0a --- /dev/null +++ b/frontend/src/assets/Icon/Food/Soup.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/Food/Sushi.svg b/frontend/src/assets/Icon/Food/Sushi.svg new file mode 100644 index 00000000..bc90f354 --- /dev/null +++ b/frontend/src/assets/Icon/Food/Sushi.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Food/VeganFood.svg b/frontend/src/assets/Icon/Food/VeganFood.svg new file mode 100644 index 00000000..18617a53 --- /dev/null +++ b/frontend/src/assets/Icon/Food/VeganFood.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/Food/VegetarianCuisine.svg b/frontend/src/assets/Icon/Food/VegetarianCuisine.svg new file mode 100644 index 00000000..91f42849 --- /dev/null +++ b/frontend/src/assets/Icon/Food/VegetarianCuisine.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Arrow-Right.svg b/frontend/src/assets/Icon/General/Arrow-Right.svg new file mode 100644 index 00000000..90ae714d --- /dev/null +++ b/frontend/src/assets/Icon/General/Arrow-Right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Edit.svg b/frontend/src/assets/Icon/General/Edit.svg new file mode 100644 index 00000000..b59f74d5 --- /dev/null +++ b/frontend/src/assets/Icon/General/Edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Minus-Border.svg b/frontend/src/assets/Icon/General/Minus-Border.svg new file mode 100644 index 00000000..f71a9eb8 --- /dev/null +++ b/frontend/src/assets/Icon/General/Minus-Border.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Plus-Border.svg b/frontend/src/assets/Icon/General/Plus-Border.svg new file mode 100644 index 00000000..88ae85ac --- /dev/null +++ b/frontend/src/assets/Icon/General/Plus-Border.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Plus.svg b/frontend/src/assets/Icon/General/Plus.svg new file mode 100644 index 00000000..9f290706 --- /dev/null +++ b/frontend/src/assets/Icon/General/Plus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Profile.svg b/frontend/src/assets/Icon/General/Profile.svg new file mode 100644 index 00000000..ca018573 --- /dev/null +++ b/frontend/src/assets/Icon/General/Profile.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/Icon/General/Recipe.png b/frontend/src/assets/Icon/General/Recipe.png new file mode 100644 index 0000000000000000000000000000000000000000..e6d10c6c2169568de4098912ca1b48a2ebee8e89 GIT binary patch literal 614 zcmV-s0-61ZP)INjq(BA@nT&HrTCgtC2&1Lizs{ z2Pe^!Tmlr5wxTU5q=Y9q1-K%q?|9eELj>vs<`t!YKf-h~gn$KSXl|;YSDrN@5Mmj9 zwN+>yVnP$*3EXprrKL`^Jyi$+0+HOqh18KaID>%P3<7eKDiXIT5Qrp^xQ#w(y(0ig zB5@nNjqa6FtVtD#+o;GKB-b=V;@sE-1o6|D*aS%A$9UYd33x$%dI?w|KgLNsFx~tl zI?&&mD!8md56JT_)R{XYRsoF^phP-UpL3VQ*8v=$CvE9U$`_S8#Z0*? zHneNkM1arVtb)ZI?Hu96NK~uMQ_Kv;z`$SR2O&j{Ref#zD*ylh07*qoM6N<$g76>u A)&Kwi literal 0 HcmV?d00001 diff --git a/frontend/src/assets/Icon/Nav/Bookmark/Active.svg b/frontend/src/assets/Icon/Nav/Bookmark/Active.svg new file mode 100644 index 00000000..8c7cf1b1 --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Bookmark/Active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Nav/Bookmark/Inactive.svg b/frontend/src/assets/Icon/Nav/Bookmark/Inactive.svg new file mode 100644 index 00000000..a48681b2 --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Bookmark/Inactive.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Nav/Home/Active.svg b/frontend/src/assets/Icon/Nav/Home/Active.svg new file mode 100644 index 00000000..1d73523b --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Home/Active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Nav/Home/Inactive.svg b/frontend/src/assets/Icon/Nav/Home/Inactive.svg new file mode 100644 index 00000000..66924b3a --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Home/Inactive.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/Icon/Nav/Notification/Active.svg b/frontend/src/assets/Icon/Nav/Notification/Active.svg new file mode 100644 index 00000000..58d589fc --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Notification/Active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Nav/Notification/Inactive.svg b/frontend/src/assets/Icon/Nav/Notification/Inactive.svg new file mode 100644 index 00000000..67535cd2 --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Notification/Inactive.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/Nav/Profile/Active.svg b/frontend/src/assets/Icon/Nav/Profile/Active.svg new file mode 100644 index 00000000..13ae2191 --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Profile/Active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/Icon/Nav/Profile/Inactive.svg b/frontend/src/assets/Icon/Nav/Profile/Inactive.svg new file mode 100644 index 00000000..cda24b5b --- /dev/null +++ b/frontend/src/assets/Icon/Nav/Profile/Inactive.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..3ff608ae --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 996641325c71660f57f157201547e0807a98f947 Mon Sep 17 00:00:00 2001 From: Nazire Date: Tue, 7 May 2024 17:49:58 +0300 Subject: [PATCH 12/74] Added Icons under assets --- frontend/src/assets/Icon/General/Allergies.svg | 6 ++++++ frontend/src/assets/Icon/General/Arrow-Left.svg | 3 +++ frontend/src/assets/Icon/General/Arrow-Right.svg | 4 +--- frontend/src/assets/Icon/General/Bookmark.svg | 5 +++++ frontend/src/assets/Icon/General/Calorie.svg | 4 ++++ frontend/src/assets/Icon/General/Clock.svg | 3 +++ frontend/src/assets/Icon/General/Close.svg | 3 +++ frontend/src/assets/Icon/General/Comments.svg | 5 +++++ frontend/src/assets/Icon/General/Edit.svg | 4 +--- frontend/src/assets/Icon/General/Filter.svg | 3 +++ frontend/src/assets/Icon/General/Food.svg | 6 ++++++ frontend/src/assets/Icon/General/Link.svg | 5 +++++ frontend/src/assets/Icon/General/Location.svg | 3 +++ frontend/src/assets/Icon/General/Minus-Border.svg | 4 +--- frontend/src/assets/Icon/General/More.svg | 10 ++++++++++ frontend/src/assets/Icon/General/Play.svg | 3 +++ frontend/src/assets/Icon/General/Plus-Border.svg | 4 +--- frontend/src/assets/Icon/General/Plus.svg | 4 +--- frontend/src/assets/Icon/General/Profile.svg | 6 +----- frontend/src/assets/Icon/General/Recipe.png | Bin 614 -> 0 bytes frontend/src/assets/Icon/General/Recipe.svg | 3 +++ frontend/src/assets/Icon/General/Search.svg | 3 +++ frontend/src/assets/Icon/General/Send.svg | 3 +++ frontend/src/assets/Icon/General/Serving.svg | 6 ++++++ frontend/src/assets/Icon/General/Star.svg | 3 +++ frontend/src/assets/Icon/General/Tick.svg | 3 +++ frontend/src/assets/Icon/General/Wikipedia.svg | 3 +++ frontend/src/components/Recipe.tsx | 0 28 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 frontend/src/assets/Icon/General/Allergies.svg create mode 100644 frontend/src/assets/Icon/General/Arrow-Left.svg create mode 100644 frontend/src/assets/Icon/General/Bookmark.svg create mode 100644 frontend/src/assets/Icon/General/Calorie.svg create mode 100644 frontend/src/assets/Icon/General/Clock.svg create mode 100644 frontend/src/assets/Icon/General/Close.svg create mode 100644 frontend/src/assets/Icon/General/Comments.svg create mode 100644 frontend/src/assets/Icon/General/Filter.svg create mode 100644 frontend/src/assets/Icon/General/Food.svg create mode 100644 frontend/src/assets/Icon/General/Link.svg create mode 100644 frontend/src/assets/Icon/General/Location.svg create mode 100644 frontend/src/assets/Icon/General/More.svg create mode 100644 frontend/src/assets/Icon/General/Play.svg delete mode 100644 frontend/src/assets/Icon/General/Recipe.png create mode 100644 frontend/src/assets/Icon/General/Recipe.svg create mode 100644 frontend/src/assets/Icon/General/Search.svg create mode 100644 frontend/src/assets/Icon/General/Send.svg create mode 100644 frontend/src/assets/Icon/General/Serving.svg create mode 100644 frontend/src/assets/Icon/General/Star.svg create mode 100644 frontend/src/assets/Icon/General/Tick.svg create mode 100644 frontend/src/assets/Icon/General/Wikipedia.svg create mode 100644 frontend/src/components/Recipe.tsx diff --git a/frontend/src/assets/Icon/General/Allergies.svg b/frontend/src/assets/Icon/General/Allergies.svg new file mode 100644 index 00000000..338f1f7b --- /dev/null +++ b/frontend/src/assets/Icon/General/Allergies.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/General/Arrow-Left.svg b/frontend/src/assets/Icon/General/Arrow-Left.svg new file mode 100644 index 00000000..5f0980fe --- /dev/null +++ b/frontend/src/assets/Icon/General/Arrow-Left.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Arrow-Right.svg b/frontend/src/assets/Icon/General/Arrow-Right.svg index 90ae714d..c3bdc29e 100644 --- a/frontend/src/assets/Icon/General/Arrow-Right.svg +++ b/frontend/src/assets/Icon/General/Arrow-Right.svg @@ -1,5 +1,3 @@ - - - + diff --git a/frontend/src/assets/Icon/General/Bookmark.svg b/frontend/src/assets/Icon/General/Bookmark.svg new file mode 100644 index 00000000..cc3d2423 --- /dev/null +++ b/frontend/src/assets/Icon/General/Bookmark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Calorie.svg b/frontend/src/assets/Icon/General/Calorie.svg new file mode 100644 index 00000000..6fdb28e5 --- /dev/null +++ b/frontend/src/assets/Icon/General/Calorie.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/Icon/General/Clock.svg b/frontend/src/assets/Icon/General/Clock.svg new file mode 100644 index 00000000..24d7f1a0 --- /dev/null +++ b/frontend/src/assets/Icon/General/Clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Close.svg b/frontend/src/assets/Icon/General/Close.svg new file mode 100644 index 00000000..54ee2960 --- /dev/null +++ b/frontend/src/assets/Icon/General/Close.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Comments.svg b/frontend/src/assets/Icon/General/Comments.svg new file mode 100644 index 00000000..70622e31 --- /dev/null +++ b/frontend/src/assets/Icon/General/Comments.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Edit.svg b/frontend/src/assets/Icon/General/Edit.svg index b59f74d5..d076026f 100644 --- a/frontend/src/assets/Icon/General/Edit.svg +++ b/frontend/src/assets/Icon/General/Edit.svg @@ -1,5 +1,3 @@ - - - + diff --git a/frontend/src/assets/Icon/General/Filter.svg b/frontend/src/assets/Icon/General/Filter.svg new file mode 100644 index 00000000..bc2a4fbc --- /dev/null +++ b/frontend/src/assets/Icon/General/Filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Food.svg b/frontend/src/assets/Icon/General/Food.svg new file mode 100644 index 00000000..041875e2 --- /dev/null +++ b/frontend/src/assets/Icon/General/Food.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/General/Link.svg b/frontend/src/assets/Icon/General/Link.svg new file mode 100644 index 00000000..e48d5d7a --- /dev/null +++ b/frontend/src/assets/Icon/General/Link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/Icon/General/Location.svg b/frontend/src/assets/Icon/General/Location.svg new file mode 100644 index 00000000..8810a0cc --- /dev/null +++ b/frontend/src/assets/Icon/General/Location.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Minus-Border.svg b/frontend/src/assets/Icon/General/Minus-Border.svg index f71a9eb8..cc6038d7 100644 --- a/frontend/src/assets/Icon/General/Minus-Border.svg +++ b/frontend/src/assets/Icon/General/Minus-Border.svg @@ -1,5 +1,3 @@ - - - + diff --git a/frontend/src/assets/Icon/General/More.svg b/frontend/src/assets/Icon/General/More.svg new file mode 100644 index 00000000..de2d3c55 --- /dev/null +++ b/frontend/src/assets/Icon/General/More.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/Icon/General/Play.svg b/frontend/src/assets/Icon/General/Play.svg new file mode 100644 index 00000000..065ff4b2 --- /dev/null +++ b/frontend/src/assets/Icon/General/Play.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Plus-Border.svg b/frontend/src/assets/Icon/General/Plus-Border.svg index 88ae85ac..ff9f0787 100644 --- a/frontend/src/assets/Icon/General/Plus-Border.svg +++ b/frontend/src/assets/Icon/General/Plus-Border.svg @@ -1,5 +1,3 @@ - - - + diff --git a/frontend/src/assets/Icon/General/Plus.svg b/frontend/src/assets/Icon/General/Plus.svg index 9f290706..5b8f2627 100644 --- a/frontend/src/assets/Icon/General/Plus.svg +++ b/frontend/src/assets/Icon/General/Plus.svg @@ -1,5 +1,3 @@ - - - + diff --git a/frontend/src/assets/Icon/General/Profile.svg b/frontend/src/assets/Icon/General/Profile.svg index ca018573..9b2f4de2 100644 --- a/frontend/src/assets/Icon/General/Profile.svg +++ b/frontend/src/assets/Icon/General/Profile.svg @@ -1,7 +1,3 @@ - - - - - + diff --git a/frontend/src/assets/Icon/General/Recipe.png b/frontend/src/assets/Icon/General/Recipe.png deleted file mode 100644 index e6d10c6c2169568de4098912ca1b48a2ebee8e89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614 zcmV-s0-61ZP)INjq(BA@nT&HrTCgtC2&1Lizs{ z2Pe^!Tmlr5wxTU5q=Y9q1-K%q?|9eELj>vs<`t!YKf-h~gn$KSXl|;YSDrN@5Mmj9 zwN+>yVnP$*3EXprrKL`^Jyi$+0+HOqh18KaID>%P3<7eKDiXIT5Qrp^xQ#w(y(0ig zB5@nNjqa6FtVtD#+o;GKB-b=V;@sE-1o6|D*aS%A$9UYd33x$%dI?w|KgLNsFx~tl zI?&&mD!8md56JT_)R{XYRsoF^phP-UpL3VQ*8v=$CvE9U$`_S8#Z0*? zHneNkM1arVtb)ZI?Hu96NK~uMQ_Kv;z`$SR2O&j{Ref#zD*ylh07*qoM6N<$g76>u A)&Kwi diff --git a/frontend/src/assets/Icon/General/Recipe.svg b/frontend/src/assets/Icon/General/Recipe.svg new file mode 100644 index 00000000..e3764d3b --- /dev/null +++ b/frontend/src/assets/Icon/General/Recipe.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Search.svg b/frontend/src/assets/Icon/General/Search.svg new file mode 100644 index 00000000..369b26ca --- /dev/null +++ b/frontend/src/assets/Icon/General/Search.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Send.svg b/frontend/src/assets/Icon/General/Send.svg new file mode 100644 index 00000000..632aa957 --- /dev/null +++ b/frontend/src/assets/Icon/General/Send.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Serving.svg b/frontend/src/assets/Icon/General/Serving.svg new file mode 100644 index 00000000..0a08011f --- /dev/null +++ b/frontend/src/assets/Icon/General/Serving.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/Icon/General/Star.svg b/frontend/src/assets/Icon/General/Star.svg new file mode 100644 index 00000000..525bb31d --- /dev/null +++ b/frontend/src/assets/Icon/General/Star.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Tick.svg b/frontend/src/assets/Icon/General/Tick.svg new file mode 100644 index 00000000..409d65f5 --- /dev/null +++ b/frontend/src/assets/Icon/General/Tick.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/Icon/General/Wikipedia.svg b/frontend/src/assets/Icon/General/Wikipedia.svg new file mode 100644 index 00000000..2ec049ef --- /dev/null +++ b/frontend/src/assets/Icon/General/Wikipedia.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/Recipe.tsx b/frontend/src/components/Recipe.tsx new file mode 100644 index 00000000..e69de29b From f243d4f990c6b9468da5c6b2ba8541e43adac13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Tue, 7 May 2024 17:59:56 +0300 Subject: [PATCH 13/74] chore(devops): use digital ocean access token --- .github/workflows/backend_deploy_staging.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backend_deploy_staging.yml b/.github/workflows/backend_deploy_staging.yml index fdcbb40b..fb04408d 100644 --- a/.github/workflows/backend_deploy_staging.yml +++ b/.github/workflows/backend_deploy_staging.yml @@ -29,8 +29,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Login to Docker Hub - run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + - name: Authorize to with doctl + run: echo ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} | doctl auth init --context github-actions + + - name: Login to Docker registry + run: doctl registry login --context github-actions - name: Build Docker image run: docker compose -f docker-compose.yml build From 73702f0980438312179e7e429f8d30392f5d4156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Ya=C5=9Far?= Date: Tue, 7 May 2024 18:21:32 +0300 Subject: [PATCH 14/74] fix(devops): update compose names --- .github/workflows/backend_build.yml | 2 +- .github/workflows/backend_deploy_staging.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backend_build.yml b/.github/workflows/backend_build.yml index 902f79a9..872c2309 100644 --- a/.github/workflows/backend_build.yml +++ b/.github/workflows/backend_build.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v4 - name: Test with Maven - run: docker compose -f docker-compose.dev.yml run --rm backend-dev mvn test + run: docker compose -f dev.yml run --rm backend mvn test diff --git a/.github/workflows/backend_deploy_staging.yml b/.github/workflows/backend_deploy_staging.yml index fb04408d..9890dc6f 100644 --- a/.github/workflows/backend_deploy_staging.yml +++ b/.github/workflows/backend_deploy_staging.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Test with Maven - run: docker compose -f docker-compose.dev.yml run --rm backend-dev mvn test + run: docker compose -f dev.yml run --rm backend mvn test deploy: needs: test @@ -36,7 +36,7 @@ jobs: run: doctl registry login --context github-actions - name: Build Docker image - run: docker compose -f docker-compose.yml build + run: docker compose build - name: Tag Docker image run: docker tag bounswe2024group1-backend:latest registry.digitalocean.com/semantic-browse/backend-staging:latest From d0412a2d1b10551975a00671da994783637b0d46 Mon Sep 17 00:00:00 2001 From: EnesBaserr Date: Tue, 7 May 2024 19:53:29 +0300 Subject: [PATCH 15/74] JWT token & Security Filter chain flow fixed for secured api routes. --- .../config/SecurityConfiguration.java | 8 ++++++++ .../cuisines/controllers/TestController.java | 19 +++++++++++++++++++ .../group1/cuisines/services/JwtService.java | 3 +++ 3 files changed, 30 insertions(+) create mode 100644 backend/src/main/java/com/group1/cuisines/controllers/TestController.java diff --git a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java index d39777e3..81d73ddf 100644 --- a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java +++ b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java @@ -1,5 +1,6 @@ package com.group1.cuisines.config; +import com.group1.cuisines.services.JwtService; import com.group1.cuisines.services.UserService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -15,6 +16,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @@ -23,6 +25,9 @@ public class SecurityConfiguration { private final UserService userService; + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -32,6 +37,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) request .requestMatchers("/api/v1/auth/**") .permitAll() // Permit all requests to "/api/v1/auth" + .requestMatchers("/api/v2/test").authenticated() .requestMatchers(("GET"), "/api/v1/**") .permitAll() // Permit all GET requests .requestMatchers("/api/v1/admin/**") @@ -45,10 +51,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) ) ) // Set session creation policy to STATELESS .authenticationProvider(authenticationProvider()); // Set authentication provider + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/backend/src/main/java/com/group1/cuisines/controllers/TestController.java b/backend/src/main/java/com/group1/cuisines/controllers/TestController.java new file mode 100644 index 00000000..2965bee3 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/controllers/TestController.java @@ -0,0 +1,19 @@ +package com.group1.cuisines.controllers; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v2") +@RequiredArgsConstructor + +public class TestController { + @GetMapping("/test") + public String test(){ + return "test"; + } + +} diff --git a/backend/src/main/java/com/group1/cuisines/services/JwtService.java b/backend/src/main/java/com/group1/cuisines/services/JwtService.java index e38b585a..270d09b6 100644 --- a/backend/src/main/java/com/group1/cuisines/services/JwtService.java +++ b/backend/src/main/java/com/group1/cuisines/services/JwtService.java @@ -1,5 +1,6 @@ package com.group1.cuisines.services; +import com.group1.cuisines.entities.User; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @@ -48,6 +49,7 @@ private String generateToken( Map extraClaims, UserDetails userDetails ) { // This method is used to generate a JWT token + return Jwts.builder() .setClaims(extraClaims) .setSubject(userDetails.getUsername()) @@ -55,6 +57,7 @@ private String generateToken( .setExpiration( new Date(System.currentTimeMillis() + 1000 * 60 * 24) ) // 24 hours + .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact(); } From f1bbada653b0f29f520324253b9841b23e64dc44 Mon Sep 17 00:00:00 2001 From: EnesBaserr Date: Tue, 7 May 2024 20:22:06 +0300 Subject: [PATCH 16/74] users/me endpoint implemented. --- .../cuisines/controllers/UserController.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 backend/src/main/java/com/group1/cuisines/controllers/UserController.java diff --git a/backend/src/main/java/com/group1/cuisines/controllers/UserController.java b/backend/src/main/java/com/group1/cuisines/controllers/UserController.java new file mode 100644 index 00000000..f2e62233 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/controllers/UserController.java @@ -0,0 +1,36 @@ +package com.group1.cuisines.controllers; + +import com.group1.cuisines.entities.User; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/users") +@RequiredArgsConstructor +public class UserController { + @GetMapping("/me") + public ResponseEntity getUserDetails(@AuthenticationPrincipal UserDetails userDetails) { + if (userDetails instanceof com.group1.cuisines.entities.User) { + User user = (User) userDetails; + Map userInfo = new HashMap<>(); + userInfo.put("username", user.getUsername()); + userInfo.put("email", user.getEmail()); + userInfo.put("bio", user.getBio()); + userInfo.put("country", user.getCountry()); + + return ResponseEntity.ok(userInfo); + } + + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User not authenticated"); + } + +} From df3cb170a9c89916a8032aea676a3fd61a451b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Tue, 7 May 2024 22:23:09 +0300 Subject: [PATCH 17/74] chore(frontend): add fetcher tests --- .../api/semanticBrowseFetcher.test.ts | 307 ++++++++++++++++++ .../src/services/api/semanticBrowseFetcher.ts | 13 +- 2 files changed, 315 insertions(+), 5 deletions(-) create mode 100644 frontend/src/services/api/semanticBrowseFetcher.test.ts diff --git a/frontend/src/services/api/semanticBrowseFetcher.test.ts b/frontend/src/services/api/semanticBrowseFetcher.test.ts new file mode 100644 index 00000000..fea85ba0 --- /dev/null +++ b/frontend/src/services/api/semanticBrowseFetcher.test.ts @@ -0,0 +1,307 @@ +import { + ArgumentsType, + Mock, + afterAll, + describe, + expect, + it, + vi, +} from "vitest"; +import { ApiResponse } from "./semanticBrowseSchemas"; +import { + FetchError, + getFieldErrors, + renderError, + semanticBrowseFetch, +} from "./semanticBrowseFetcher"; + +vi.stubGlobal("fetch", vi.fn()); +fetch; +afterAll(() => { + vi.unstubAllGlobals(); +}); + +const createResponse = (status: number, val: ApiResponse) => + ({ + ok: status < 400, + status, + headers: { + get: vi.fn().mockReturnValue("application/json"), + }, + json() { + return Promise.resolve(val); + }, + }) as unknown as Response; + +const fetchMock = fetch as unknown as Mock< + ArgumentsType, + ReturnType +>; + +describe("API Fetcher", () => { + describe("on error", () => { + it("should forward an API error", () => { + fetchMock.mockResolvedValueOnce( + createResponse(200, { + status: 400, + errors: [ + { + message: "Bad request", + }, + ], + }), + ); + + expect( + semanticBrowseFetch({ + url: "/api/v1/hello", + method: "GET", + body: {}, + }), + ).to.rejects.toMatchObject({ + status: 400, + payload: { + status: 400, + errors: [ + { + message: "Bad request", + }, + ], + }, + }); + }); + + it("should handle an error HTTP status code", () => { + fetchMock.mockResolvedValueOnce( + createResponse(500, { + status: 500, + errors: [ + { + message: "Internal server error", + }, + ], + }), + ); + + expect( + semanticBrowseFetch({ + url: "/api/v1/hello", + method: "GET", + body: {}, + }), + ).to.rejects.toMatchObject({ + status: 500, + payload: { + status: 500, + errors: [ + { + message: "Internal server error", + }, + ], + }, + }); + }); + + it("should handle a rejection when fetching", () => { + fetchMock.mockRejectedValueOnce(new Error("Network error")); + + expect( + semanticBrowseFetch({ + url: "/api/v1/hello", + method: "GET", + body: {}, + }), + ).to.rejects.toMatchObject({ + status: 500, + errors: [ + { + message: expect.stringContaining("Network error"), + }, + ], + }); + }); + }); + + describe("on success", () => { + it("should return the data", async () => { + fetchMock.mockResolvedValueOnce( + createResponse(200, { + status: 200, + data: { + hello: "world", + }, + }), + ); + + expect( + await semanticBrowseFetch({ + url: "/api/v1/hello", + method: "GET", + body: {}, + }), + ).toEqual({ + status: 200, + data: { + hello: "world", + }, + }); + }); + + it("should accept 201 Created status", () => { + fetchMock.mockResolvedValueOnce( + createResponse(201, { + status: 201, + data: { + hello: "world", + }, + }), + ); + + expect( + semanticBrowseFetch({ + url: "/api/v1/hello", + method: "GET", + body: {}, + }), + ).resolves.toEqual({ + status: 201, + data: { + hello: "world", + }, + }); + }); + }); + + describe("fetch call", () => { + it("should call fetch with the right arguments", async () => { + fetchMock.mockResolvedValueOnce( + createResponse(200, { + status: 200, + data: { + hello: "world", + }, + }), + ); + + await semanticBrowseFetch({ + url: "/hello", + method: "GET", + body: {}, + }); + + expect(fetch).toHaveBeenCalledWith( + "/api/v1/hello", + expect.objectContaining({ + method: "GET", + body: "{}", + headers: { + "Content-Type": "application/json", + }, + }), + ); + }); + }); +}); + +const simpleError = { + status: 400, + payload: { + status: 400, + errors: [ + { + message: "Bad request", + }, + ], + }, +} satisfies FetchError; + +const multipleErrors = { + status: 400, + payload: { + status: 400, + errors: [ + { + message: "Bad request", + }, + { + message: "Invalid email", + }, + ], + }, +} satisfies FetchError; + +const formError = { + status: 400, + payload: { + status: 400, + errors: [ + { + message: "Invalid email", + field: "email", + }, + { + message: "Password too short", + field: "password", + }, + ], + }, +} satisfies FetchError; + +const allErrors = { + status: 400, + payload: { + status: 400, + errors: [ + { + message: "Bad request", + }, + { + message: "Invalid email", + field: "email", + }, + { + message: "Password too short", + field: "password", + }, + ], + }, +} satisfies FetchError; + +describe("renderErrors", () => { + it("should not throw ", () => { + renderError(simpleError); + }); + + it("should render general errors", () => { + expect(renderError(simpleError)).toEqual("Bad request"); + }); + + it("should render multiple errors", () => { + expect(renderError(multipleErrors)).toEqual("Bad request\nInvalid email"); + }); + + it("should render form errors when there are general errors", () => { + expect(renderError(allErrors)).toEqual( + "Bad request\n\nField errors:\nemail: Invalid email\npassword: Password too short", + ); + }); + + it("should render form errors", () => { + expect(renderError(formError)).toEqual( + "Field errors:\nemail: Invalid email\npassword: Password too short", + ); + }); +}); + +describe("getFieldErrors", () => { + it("should not return general errors", () => { + expect(getFieldErrors(simpleError)).toEqual({}); + }); + + it("should return field errors", () => { + expect(getFieldErrors(formError)).toEqual({ + email: "Invalid email", + password: "Password too short", + }); + }); +}); diff --git a/frontend/src/services/api/semanticBrowseFetcher.ts b/frontend/src/services/api/semanticBrowseFetcher.ts index 6e6b42b9..4bdf149c 100644 --- a/frontend/src/services/api/semanticBrowseFetcher.ts +++ b/frontend/src/services/api/semanticBrowseFetcher.ts @@ -177,16 +177,19 @@ export const renderError = ( } const errors = error.payload.errors; - const fieldErrors = errors.filter((e) => !!e.field); - const generalErrors = errors.filter((e) => !e.field); + const fieldErrors = errors + .filter((e) => !!e.field) + .map((e) => e.field + ": " + e.message); + const generalErrors = errors.filter((e) => !e.field).map((e) => e.message); const renderedString = errors.length > 0 ? generalErrors.join("\n") + - "\n" + - (fieldErrors.length ? "Field errors:\n" + fieldErrors.join("\n") : "") + (fieldErrors.length + ? "\n\nField errors:\n" + fieldErrors.join("\n") + : "") : "Unknown error."; - return renderedString; + return renderedString.trim(); }; export const getFieldErrors = ( From 3c48620f71c0867a8d270541dc5aa63daa08fdeb Mon Sep 17 00:00:00 2001 From: EnesBaserr Date: Tue, 7 May 2024 22:57:03 +0300 Subject: [PATCH 18/74] Entity classes are created & mappings between those entities handled. Corresponding JPA repository interfaces are created. --- backend/pom.xml | 10 +++++- .../group1/cuisines/entities/Bookmark.java | 24 +++++++++++++ .../com/group1/cuisines/entities/Comment.java | 34 +++++++++++++++++++ .../com/group1/cuisines/entities/Cuisine.java | 33 ++++++++++++++++++ .../com/group1/cuisines/entities/Dish.java | 13 +++++-- .../group1/cuisines/entities/Ingredient.java | 21 ++++++++++++ .../com/group1/cuisines/entities/Rating.java | 26 ++++++++++++++ .../com/group1/cuisines/entities/Recipe.java | 32 +++++++++++++++++ .../com/group1/cuisines/entities/Upvote.java | 24 +++++++++++++ .../repositories/BookmarkRepository.java | 11 ++++++ .../repositories/CommentRepository.java | 10 ++++++ .../repositories/CuisineRepository.java | 9 +++++ .../cuisines/repositories/DishRepository.java | 10 ++++++ .../repositories/IngredientsRepository.java | 9 +++++ .../repositories/RatingRepository.java | 10 ++++++ .../repositories/RecipeRepository.java | 9 +++++ .../repositories/UpvoteRepository.java | 9 +++++ .../cuisines/services/WikidataService.java | 8 ++--- .../src/main/resources/application.properties | 3 +- backend/src/main/resources/banner.txt | 2 ++ 20 files changed, 299 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Bookmark.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Comment.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Cuisine.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Ingredient.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Rating.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Recipe.java create mode 100644 backend/src/main/java/com/group1/cuisines/entities/Upvote.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/BookmarkRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/CommentRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/CuisineRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/DishRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/IngredientsRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/RatingRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/RecipeRepository.java create mode 100644 backend/src/main/java/com/group1/cuisines/repositories/UpvoteRepository.java diff --git a/backend/pom.xml b/backend/pom.xml index 21bc194f..470caae3 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -65,6 +65,13 @@ jjwt-api 0.11.2 + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + io.jsonwebtoken jjwt-impl @@ -80,6 +87,7 @@ spring-boot-devtools true + @@ -88,7 +96,7 @@ org.springframework.boot spring-boot-maven-plugin - true + org.projectlombok diff --git a/backend/src/main/java/com/group1/cuisines/entities/Bookmark.java b/backend/src/main/java/com/group1/cuisines/entities/Bookmark.java new file mode 100644 index 00000000..53e15e7a --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Bookmark.java @@ -0,0 +1,24 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "bookmarks") +public class Bookmark { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "recipe_id", nullable = false) + private Recipe recipe; +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/Comment.java b/backend/src/main/java/com/group1/cuisines/entities/Comment.java new file mode 100644 index 00000000..3645be60 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Comment.java @@ -0,0 +1,34 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "comments") +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "recipe_id", nullable = false) + private Recipe recipe; + + @Column(length = 1024) // Adjust length as necessary + private String text; + + private LocalDateTime createdDate = LocalDateTime.now(); // Automatically set the date when the comment is created + @OneToMany(mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true) + private Set upvotes; +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/Cuisine.java b/backend/src/main/java/com/group1/cuisines/entities/Cuisine.java new file mode 100644 index 00000000..8a9eb463 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Cuisine.java @@ -0,0 +1,33 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.net.URL; +import java.util.List; + + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "cuisines") +public class Cuisine { + @Id + private String id; + private String name; + private String description; + private URL wikipediaLink; + + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable( + name = "cuisine_dish", + joinColumns = @JoinColumn(name = "cuisine_id"), + inverseJoinColumns = @JoinColumn(name = "dish_id") + ) + private List dishes; +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/Dish.java b/backend/src/main/java/com/group1/cuisines/entities/Dish.java index 2521affb..4434fde4 100644 --- a/backend/src/main/java/com/group1/cuisines/entities/Dish.java +++ b/backend/src/main/java/com/group1/cuisines/entities/Dish.java @@ -1,17 +1,22 @@ package com.group1.cuisines.entities; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Builder.Default; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; + @Data @Builder @NoArgsConstructor @AllArgsConstructor +@Entity +@Table(name = "dishes") public class Dish { - + @Id private String id; private String name; private String description; @@ -19,5 +24,9 @@ public class Dish { private String countries; private String ingredients; private String foodTypes; - private String cuisines; + private String cuisine; + + + @ManyToMany(mappedBy = "dishes") + private List cuisines; } diff --git a/backend/src/main/java/com/group1/cuisines/entities/Ingredient.java b/backend/src/main/java/com/group1/cuisines/entities/Ingredient.java new file mode 100644 index 00000000..4de0ede8 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Ingredient.java @@ -0,0 +1,21 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "ingredients") +public class Ingredient { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String name; + private String description; // Optional, can include details like organic, gluten-free, etc. + @ManyToOne + @JoinColumn(name = "recipe_id") // This column in the Ingredient table will hold the foreign key to the Recipe + private Recipe recipe; // This is the 'recipe' field expected by the 'mappedBy' attribute +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/Rating.java b/backend/src/main/java/com/group1/cuisines/entities/Rating.java new file mode 100644 index 00000000..c11ce612 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Rating.java @@ -0,0 +1,26 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "ratings") +public class Rating { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "recipe_id", nullable = false) + private Recipe recipe; + + private int ratingValue; // Assuming ratings are integer values +} diff --git a/backend/src/main/java/com/group1/cuisines/entities/Recipe.java b/backend/src/main/java/com/group1/cuisines/entities/Recipe.java new file mode 100644 index 00000000..be7c5138 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Recipe.java @@ -0,0 +1,32 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "recipes") +public class Recipe { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String title; + private String instructions; + private int preparationTime; + private int cookingTime; + + @ManyToOne + @JoinColumn(name = "dish_id", nullable = false) + private Dish dish; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List ingredients; // This assumes a direct one-to-many relationship +} \ No newline at end of file diff --git a/backend/src/main/java/com/group1/cuisines/entities/Upvote.java b/backend/src/main/java/com/group1/cuisines/entities/Upvote.java new file mode 100644 index 00000000..24b44d2e --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/entities/Upvote.java @@ -0,0 +1,24 @@ +package com.group1.cuisines.entities; + +import jakarta.persistence.*; +import lombok.*; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "upvotes") +public class Upvote { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "comment_id", nullable = false) + private Comment comment; +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/BookmarkRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/BookmarkRepository.java new file mode 100644 index 00000000..1cf1858a --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/BookmarkRepository.java @@ -0,0 +1,11 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Bookmark; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookmarkRepository extends JpaRepository { +} + + diff --git a/backend/src/main/java/com/group1/cuisines/repositories/CommentRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/CommentRepository.java new file mode 100644 index 00000000..33a3b141 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/CommentRepository.java @@ -0,0 +1,10 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/CuisineRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/CuisineRepository.java new file mode 100644 index 00000000..1a6a89ef --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/CuisineRepository.java @@ -0,0 +1,9 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Cuisine; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CuisineRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/DishRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/DishRepository.java new file mode 100644 index 00000000..d8494315 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/DishRepository.java @@ -0,0 +1,10 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Dish; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface DishRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/IngredientsRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/IngredientsRepository.java new file mode 100644 index 00000000..1712bbf7 --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/IngredientsRepository.java @@ -0,0 +1,9 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Ingredient; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface IngredientsRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/RatingRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/RatingRepository.java new file mode 100644 index 00000000..4cd68d6b --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/RatingRepository.java @@ -0,0 +1,10 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Rating; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RatingRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/RecipeRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/RecipeRepository.java new file mode 100644 index 00000000..99f36dfc --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/RecipeRepository.java @@ -0,0 +1,9 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Recipe; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RecipeRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/group1/cuisines/repositories/UpvoteRepository.java b/backend/src/main/java/com/group1/cuisines/repositories/UpvoteRepository.java new file mode 100644 index 00000000..6e1e3c1d --- /dev/null +++ b/backend/src/main/java/com/group1/cuisines/repositories/UpvoteRepository.java @@ -0,0 +1,9 @@ +package com.group1.cuisines.repositories; + +import com.group1.cuisines.entities.Upvote; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UpvoteRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/group1/cuisines/services/WikidataService.java b/backend/src/main/java/com/group1/cuisines/services/WikidataService.java index 2b31798a..8076487b 100644 --- a/backend/src/main/java/com/group1/cuisines/services/WikidataService.java +++ b/backend/src/main/java/com/group1/cuisines/services/WikidataService.java @@ -140,7 +140,7 @@ public ArrayList retrieveDishAndCuisineData(String parameter) { .countries(countries) .ingredients(ingredients) .foodTypes(foodTypes) - .cuisines(cuisines) + .cuisine(cuisines) .build() ); } @@ -188,18 +188,18 @@ public Mono retrieveDishAndCuisineData() { String countryOfOriginLabel = soln .get("countryOfOriginLabel") .toString(); - logger.info( + /* logger.info( "Dish: {}, Dish Label: {}, Image: {}, Country of Origin: {}", dish, dishLabel, image, countryOfOriginLabel - ); + );*/ } // Close the query execution queryExecution.close(); - logger.info("Query executed successfully!"); + // logger.info("Query executed successfully!"); return "Query executed successfully!"; }); mono.subscribe(); diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index a55035e9..7c41c806 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -3,7 +3,7 @@ server.port=8080 jwt.signing.key=413F4428472B4B6250655368566D5970337336763979244226452948404D6351 spring.application.name=cuisines -spring.datasource.url=jdbc:mysql://localhost:3306/cuisines-test +spring.datasource.url=jdbc:mysql://localhost:3306/cuisines-test?createDatabaseIfNotExist=true spring.datasource.username=root spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver @@ -11,4 +11,5 @@ spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true logging.level.com.group1.cuisines=DEBUG + spring.banner.location=./banner.txt diff --git a/backend/src/main/resources/banner.txt b/backend/src/main/resources/banner.txt index fdad15ba..196c2c52 100644 --- a/backend/src/main/resources/banner.txt +++ b/backend/src/main/resources/banner.txt @@ -16,6 +16,8 @@ - Cagatay Colak - Yigit Memceroktay +FOR POSTMAN COLLECTION NAVIGATE TO : https://app.getpostman.com/join-team?invite_code=139350c8bc4f87f5f7f8da08a4386a38&target_code=498dc1ff0abd5c8271fc9c745e4428a7 + ${application.title} ${application.version} Powered by Spring Boot ${spring-boot.version} From e1f576bde0fdc9c2530afd554d5307516065891e Mon Sep 17 00:00:00 2001 From: EnesBaserr Date: Wed, 8 May 2024 00:10:08 +0300 Subject: [PATCH 19/74] Request matchers defined explicitly so invalid routes does not trigger Authentication filters --- .../com/group1/cuisines/config/SecurityConfiguration.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java index 81d73ddf..e3bcc46f 100644 --- a/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java +++ b/backend/src/main/java/com/group1/cuisines/config/SecurityConfiguration.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; @@ -38,11 +39,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) .requestMatchers("/api/v1/auth/**") .permitAll() // Permit all requests to "/api/v1/auth" .requestMatchers("/api/v2/test").authenticated() - .requestMatchers(("GET"), "/api/v1/**") + .requestMatchers(("GET"), "/**") .permitAll() // Permit all GET requests .requestMatchers("/api/v1/admin/**") .hasRole("ADMIN") // Require ADMIN role for "/api/v1/resources" - .anyRequest() + .requestMatchers(HttpMethod.POST,"/**") .authenticated()) // Require authentication for all other requests .sessionManagement( manager -> From 115c183cd63a545a329ef8c2787dde1f9d0f129c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Wed, 8 May 2024 09:48:18 +0300 Subject: [PATCH 20/74] fix(frontend): some codegen types were fixed --- frontend/package.json | 2 +- frontend/src/components/FullscreenLoading.tsx | 16 ++++- frontend/src/components/SearchBar.test.tsx | 2 - frontend/src/components/ui/alert.tsx | 59 +++++++++++++++++++ frontend/src/routes/index.tsx | 3 +- frontend/src/routes/search.tsx | 51 ++++++++++++---- .../services/api/semanticBrowseComponents.ts | 12 ++-- .../src/services/api/semanticBrowseContext.ts | 10 +++- .../src/services/api/semanticBrowseFetcher.ts | 15 +---- .../src/services/api/semanticBrowseSchemas.ts | 15 +++++ swagger/openapi.yml | 41 +++++++------ 11 files changed, 167 insertions(+), 59 deletions(-) create mode 100644 frontend/src/components/ui/alert.tsx diff --git a/frontend/package.json b/frontend/package.json index c3d7e81c..61f7b07c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -69,7 +69,7 @@ "vitest": "^1.5.2" }, "hooks": { - "pre-commit": "lint-staged" + "pre-commit": "lint-staged && tsc" }, "lint-staged": { "*/**/*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix", "eslint"], diff --git a/frontend/src/components/FullscreenLoading.tsx b/frontend/src/components/FullscreenLoading.tsx index 6e6ec667..c5f638c9 100644 --- a/frontend/src/components/FullscreenLoading.tsx +++ b/frontend/src/components/FullscreenLoading.tsx @@ -1,7 +1,12 @@ +import clsx from "clsx"; import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; -export const FullscreenLoading = () => { +export const FullscreenLoading = ({ + overlay = false, +}: { + overlay?: boolean; +}) => { const [takingLong, setTakingLong] = useState(false); useEffect(() => { @@ -12,10 +17,15 @@ export const FullscreenLoading = () => { }, []); return ( -
+
{takingLong && ( -
+
This is taking a while...
)} diff --git a/frontend/src/components/SearchBar.test.tsx b/frontend/src/components/SearchBar.test.tsx index 48866f55..6db1005b 100644 --- a/frontend/src/components/SearchBar.test.tsx +++ b/frontend/src/components/SearchBar.test.tsx @@ -17,11 +17,9 @@ test("searching something goes to /search", async () => { // Act const search = screen.getAllByPlaceholderText("Search for dishes...")[0]; - screen.debug(search); fireEvent.change(search, { target: { value: "hello" } }); const button = screen.getAllByRole("button", { name: /search/i })[0]; - screen.debug(button); fireEvent.click(button); // Assert diff --git a/frontend/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx new file mode 100644 index 00000000..13219e77 --- /dev/null +++ b/frontend/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)); +Alert.displayName = "Alert"; + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertTitle.displayName = "AlertTitle"; + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertDescription.displayName = "AlertDescription"; + +export { Alert, AlertTitle, AlertDescription }; diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 21d594e8..c530bdc3 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -3,7 +3,7 @@ import Login, { action as loginAction } from "./login"; import Signup, { action as signupAction } from "./signup"; import { IndexRoute } from "./home"; import { signout } from "../services/auth"; -import { Search, loader as searchLoader } from "./search"; +import { Search } from "./search"; import { NavbarLayout } from "../components/NavbarLayout"; export const routes: RouteObject[] = [ @@ -19,7 +19,6 @@ export const routes: RouteObject[] = [ }, { path: "/search", - loader: searchLoader, Component: Search, }, { diff --git a/frontend/src/routes/search.tsx b/frontend/src/routes/search.tsx index 771ce96c..8480e9c1 100644 --- a/frontend/src/routes/search.tsx +++ b/frontend/src/routes/search.tsx @@ -1,26 +1,51 @@ -import { LoaderFunctionArgs, useLoaderData } from "react-router-dom"; +import { useSearchParams } from "react-router-dom"; import { Dish } from "../components/Dish"; -import { fetchSearchDishes } from "../services/api/semanticBrowseComponents"; +import { + SearchDishesResponse, + useSearchDishes, +} from "../services/api/semanticBrowseComponents"; +import { renderError } from "../services/api/semanticBrowseFetcher"; +import { FullscreenLoading } from "../components/FullscreenLoading"; +import { Alert, AlertDescription, AlertTitle } from "../components/ui/alert"; +import { AlertCircle } from "lucide-react"; -export const loader = ({ request }: LoaderFunctionArgs) => { - const params = new URL(request.url).searchParams; - return fetchSearchDishes({ - queryParams: { q: params.get("q") ?? undefined }, +export const Search = () => { + const [params] = useSearchParams(); + const { + data: searchResult, + isLoading, + error, + } = useSearchDishes({ + queryParams: { q: params.get("q") ?? "" }, }); -}; -export const Search = () => { - const { data: searchResult } = useLoaderData() as Awaited< - ReturnType - >; + if (isLoading) { + return ; + } + if (error) { + return ( +
+ + + Error + {renderError(error)} + +
+ ); + } return (

- Found {searchResult.length} results + {searchResult?.data?.length + ? `Found ${searchResult.data.length} results` + : "No dishes found"}

+ {!searchResult?.data?.length && ( +

Try searching for "japan", or "sausage"

+ )}
- {searchResult.map((dish) => ( + {searchResult?.data?.map((dish) => ( | Schemas.UserSummary[]; + data: Schemas.UserArray; }; export type GetUserFollowingVariables = { @@ -584,7 +584,7 @@ export type GetUserFollowersResponse = { * @example 201 */ status: 200 | 201; - data: Record | Schemas.UserSummary[]; + data: Schemas.UserArray; }; export type GetUserFollowersVariables = { @@ -759,7 +759,7 @@ export type SearchUsersResponse = { * @example 201 */ status: 200 | 201; - data: Record | Schemas.UserSummary[]; + data: Schemas.UserArray; }; export type SearchUsersVariables = { @@ -813,6 +813,7 @@ export type SearchDishesError = Fetcher.ErrorWrapper<{ }>; export type SearchDishesResponse = { + data: Schemas.DishArray; /** * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. * @@ -820,7 +821,6 @@ export type SearchDishesResponse = { * @example 201 */ status: 200 | 201; - data: Record | Schemas.DishDetails[]; }; export type SearchDishesVariables = { @@ -1012,7 +1012,7 @@ export type GetRecipesForEntityResponse = { * @example 201 */ status: 200 | 201; - data: Record | Schemas.RecipeSummary[]; + data: Schemas.RecipeArray; }; export type GetRecipesForEntityVariables = { @@ -1366,7 +1366,7 @@ export type GetFeedResponse = { * @example 201 */ status: 200 | 201; - data: Record | Schemas.RecipeSummary[]; + data: Schemas.RecipeArray; }; export type GetFeedVariables = { diff --git a/frontend/src/services/api/semanticBrowseContext.ts b/frontend/src/services/api/semanticBrowseContext.ts index b5b1b202..c33dc7f3 100644 --- a/frontend/src/services/api/semanticBrowseContext.ts +++ b/frontend/src/services/api/semanticBrowseContext.ts @@ -1,5 +1,6 @@ import type { QueryKey, UseQueryOptions } from "@tanstack/react-query"; import { QueryOperation } from "./semanticBrowseComponents"; +import useAuthStore from "../auth"; export type SemanticBrowseContext = { fetcherOptions: { @@ -42,8 +43,15 @@ export function useSemanticBrowseContext< "queryKey" | "queryFn" >, ): SemanticBrowseContext { + const token = useAuthStore.getState().token; return { - fetcherOptions: {}, + fetcherOptions: { + headers: token + ? { + Authorization: `Bearer ${token}`, + } + : {}, + }, queryOptions: {}, queryKeyFn, }; diff --git a/frontend/src/services/api/semanticBrowseFetcher.ts b/frontend/src/services/api/semanticBrowseFetcher.ts index 4bdf149c..24caf4d0 100644 --- a/frontend/src/services/api/semanticBrowseFetcher.ts +++ b/frontend/src/services/api/semanticBrowseFetcher.ts @@ -31,15 +31,6 @@ export type SemanticBrowseFetcherOptions< signal?: AbortSignal; } & SemanticBrowseContext["fetcherOptions"]; -type CleanupInnerData = - // remove inner record - { - status: TData["status"]; - data: Extract extends never - ? TData["data"] - : Extract; - }; - export async function semanticBrowseFetch< TData extends SuccessResponseObject, TError extends { status: number | "unknown"; payload: ErrorResponseObject }, @@ -61,7 +52,7 @@ export async function semanticBrowseFetch< THeaders, TQueryParams, TPathParams ->): Promise> { +>): Promise { try { const requestHeaders: HeadersInit = { "Content-Type": "application/json", @@ -131,10 +122,10 @@ export async function semanticBrowseFetch< }; } - return data as CleanupInnerData; + return data as TData; } else { // if it is not a json response, assume it is a blob and cast it to TData - return (await response.blob()) as unknown as CleanupInnerData; + return (await response.blob()) as unknown as TData; } } catch (e) { if (e instanceof Error) { diff --git a/frontend/src/services/api/semanticBrowseSchemas.ts b/frontend/src/services/api/semanticBrowseSchemas.ts index b7a10b0a..27b350ca 100644 --- a/frontend/src/services/api/semanticBrowseSchemas.ts +++ b/frontend/src/services/api/semanticBrowseSchemas.ts @@ -216,3 +216,18 @@ export type ErrorResponseObject = { status: 400 | 401 | 403 | 404 | 409 | 500; errors: ApiError[]; }; + +/** + * An array of dishes + */ +export type DishArray = DishDetails[]; + +/** + * An array of recipes + */ +export type RecipeArray = RecipeDetails[]; + +/** + * An array of users + */ +export type UserArray = UserSummary[]; diff --git a/swagger/openapi.yml b/swagger/openapi.yml index fa0d9b9e..be9904f0 100644 --- a/swagger/openapi.yml +++ b/swagger/openapi.yml @@ -310,9 +310,7 @@ paths: - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/UserSummary" + $ref: "#/components/schemas/UserArray" "400": $ref: "#/components/responses/BadRequestResponse" @@ -339,9 +337,7 @@ paths: - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/UserSummary" + $ref: "#/components/schemas/UserArray" "400": $ref: "#/components/responses/BadRequestResponse" @@ -417,9 +413,7 @@ paths: - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/UserSummary" + $ref: "#/components/schemas/UserArray" "400": $ref: "#/components/responses/BadRequestResponse" @@ -451,13 +445,11 @@ paths: application/json: schema: allOf: - - $ref: "#/components/schemas/SuccessResponseObject" - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/DishDetails" + $ref: "#/components/schemas/DishArray" + - $ref: "#/components/schemas/SuccessResponseObject" "400": $ref: "#/components/responses/BadRequestResponse" @@ -550,9 +542,7 @@ paths: - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/RecipeSummary" + $ref: "#/components/schemas/RecipeArray" "400": $ref: "#/components/responses/BadRequestResponse" @@ -710,9 +700,7 @@ paths: - type: object properties: data: - type: array - items: - $ref: "#/components/schemas/RecipeSummary" + $ref: "#/components/schemas/RecipeArray" "400": $ref: "#/components/responses/BadRequestResponse" @@ -1204,6 +1192,21 @@ components: type: array items: $ref: "#/components/schemas/ApiError" + DishArray: + description: An array of dishes + type: array + items: + $ref: "#/components/schemas/DishDetails" + RecipeArray: + description: An array of recipes + type: array + items: + $ref: "#/components/schemas/RecipeDetails" + UserArray: + description: An array of users + type: array + items: + $ref: "#/components/schemas/UserSummary" responses: OkResponse: description: OK From 144716fb55db77c856edf25359538fa2b840a308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Wed, 8 May 2024 13:41:30 +0300 Subject: [PATCH 21/74] chore(api): fix some api spec problems --- .../services/api/semanticBrowseComponents.ts | 578 ++++++++++++++++-- .../services/api/semanticBrowseResponses.ts | 12 +- .../src/services/api/semanticBrowseSchemas.ts | 81 ++- swagger/openapi.yml | 385 ++++++++++-- 4 files changed, 938 insertions(+), 118 deletions(-) diff --git a/frontend/src/services/api/semanticBrowseComponents.ts b/frontend/src/services/api/semanticBrowseComponents.ts index 4d54da97..413becd9 100644 --- a/frontend/src/services/api/semanticBrowseComponents.ts +++ b/frontend/src/services/api/semanticBrowseComponents.ts @@ -20,7 +20,7 @@ export type LogoutError = Fetcher.ErrorWrapper<{ export type LogoutResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -60,7 +60,7 @@ export type VerifyEmailError = Fetcher.ErrorWrapper<{ export type VerifyEmailResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -119,7 +119,7 @@ export type ResendVerificationEmailError = Fetcher.ErrorWrapper<{ export type ResendVerificationEmailResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -178,7 +178,7 @@ export type ResetPasswordRequestError = Fetcher.ErrorWrapper<{ export type ResetPasswordRequestResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -248,7 +248,7 @@ export type ResetPasswordError = Fetcher.ErrorWrapper<{ export type ResetPasswordResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -349,7 +349,7 @@ export type LoginError = Fetcher.ErrorWrapper<{ export type LoginResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -392,7 +392,7 @@ export type GetUserByIdError = Fetcher.ErrorWrapper<{ export type GetUserByIdResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -451,7 +451,7 @@ export type UpdateUserByIdError = Fetcher.ErrorWrapper<{ export type UpdateUserByIdResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -465,6 +465,9 @@ export type UpdateUserByIdVariables = { pathParams: UpdateUserByIdPathParams; } & SemanticBrowseContext["fetcherOptions"]; +/** + * Can only update own profile + */ export const fetchUpdateUserById = ( variables: UpdateUserByIdVariables, signal?: AbortSignal, @@ -478,6 +481,9 @@ export const fetchUpdateUserById = ( UpdateUserByIdPathParams >({ url: "/users/{userId}", method: "put", ...variables, signal }); +/** + * Can only update own profile + */ export const useUpdateUserById = ( options?: Omit< reactQuery.UseMutationOptions< @@ -511,7 +517,7 @@ export type GetUserFollowingError = Fetcher.ErrorWrapper<{ export type GetUserFollowingResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -578,7 +584,7 @@ export type GetUserFollowersError = Fetcher.ErrorWrapper<{ export type GetUserFollowersResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -634,6 +640,10 @@ export const useGetUserFollowers = ( }); }; +export type FollowUserPathParams = { + userId: number; +}; + export type FollowUserError = Fetcher.ErrorWrapper< | { status: 400; @@ -645,12 +655,19 @@ export type FollowUserError = Fetcher.ErrorWrapper< } >; -export type FollowUserRequestBody = { - followingUserId?: number; +export type FollowUserResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.UserProfile; }; export type FollowUserVariables = { - body?: FollowUserRequestBody; + pathParams: FollowUserPathParams; } & SemanticBrowseContext["fetcherOptions"]; export const fetchFollowUser = ( @@ -658,18 +675,18 @@ export const fetchFollowUser = ( signal?: AbortSignal, ) => semanticBrowseFetch< - Responses.OkResponse, + FollowUserResponse, FollowUserError, - FollowUserRequestBody, + undefined, {}, {}, - {} - >({ url: "/users/follow", method: "post", ...variables, signal }); + FollowUserPathParams + >({ url: "/users/{userId}/follow", method: "post", ...variables, signal }); export const useFollowUser = ( options?: Omit< reactQuery.UseMutationOptions< - Responses.OkResponse, + FollowUserResponse, FollowUserError, FollowUserVariables >, @@ -678,7 +695,7 @@ export const useFollowUser = ( ) => { const { fetcherOptions } = useSemanticBrowseContext(); return reactQuery.useMutation< - Responses.OkResponse, + FollowUserResponse, FollowUserError, FollowUserVariables >({ @@ -688,6 +705,10 @@ export const useFollowUser = ( }); }; +export type UnfollowUserPathParams = { + userId: number; +}; + export type UnfollowUserError = Fetcher.ErrorWrapper< | { status: 400; @@ -699,12 +720,19 @@ export type UnfollowUserError = Fetcher.ErrorWrapper< } >; -export type UnfollowUserRequestBody = { - followingUserId?: number; +export type UnfollowUserResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.UserProfile; }; export type UnfollowUserVariables = { - body?: UnfollowUserRequestBody; + pathParams: UnfollowUserPathParams; } & SemanticBrowseContext["fetcherOptions"]; export const fetchUnfollowUser = ( @@ -712,18 +740,18 @@ export const fetchUnfollowUser = ( signal?: AbortSignal, ) => semanticBrowseFetch< - Responses.OkResponse, + UnfollowUserResponse, UnfollowUserError, - UnfollowUserRequestBody, + undefined, {}, {}, - {} - >({ url: "/users/unfollow", method: "post", ...variables, signal }); + UnfollowUserPathParams + >({ url: "/users/{userId}/follow", method: "delete", ...variables, signal }); export const useUnfollowUser = ( options?: Omit< reactQuery.UseMutationOptions< - Responses.OkResponse, + UnfollowUserResponse, UnfollowUserError, UnfollowUserVariables >, @@ -732,7 +760,7 @@ export const useUnfollowUser = ( ) => { const { fetcherOptions } = useSemanticBrowseContext(); return reactQuery.useMutation< - Responses.OkResponse, + UnfollowUserResponse, UnfollowUserError, UnfollowUserVariables >({ @@ -753,7 +781,7 @@ export type SearchUsersError = Fetcher.ErrorWrapper<{ export type SearchUsersResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -815,7 +843,7 @@ export type SearchDishesError = Fetcher.ErrorWrapper<{ export type SearchDishesResponse = { data: Schemas.DishArray; /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -873,7 +901,7 @@ export type GetDishByIdError = Fetcher.ErrorWrapper<{ export type GetDishByIdResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -936,7 +964,7 @@ export type GetCuisineByIdError = Fetcher.ErrorWrapper<{ export type GetCuisineByIdResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -993,6 +1021,76 @@ export const useGetCuisineById = ( }); }; +export type FollowCuisinePathParams = { + cuisineId: number; +}; + +export type FollowCuisineError = Fetcher.ErrorWrapper< + | { + status: 400; + payload: Responses.BadRequestResponse; + } + | { + status: 404; + payload: Responses.NotFoundResponse; + } +>; + +export type FollowCuisineResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.CuisineDetails; +}; + +export type FollowCuisineVariables = { + pathParams: FollowCuisinePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchFollowCuisine = ( + variables: FollowCuisineVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + FollowCuisineResponse, + FollowCuisineError, + undefined, + {}, + {}, + FollowCuisinePathParams + >({ + url: "/cuisines/{cuisineId}/follow", + method: "post", + ...variables, + signal, + }); + +export const useFollowCuisine = ( + options?: Omit< + reactQuery.UseMutationOptions< + FollowCuisineResponse, + FollowCuisineError, + FollowCuisineVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + FollowCuisineResponse, + FollowCuisineError, + FollowCuisineVariables + >({ + mutationFn: (variables: FollowCuisineVariables) => + fetchFollowCuisine({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + export type GetRecipesForEntityQueryParams = { sort?: "recent" | "topRated"; dishId?: number; @@ -1006,7 +1104,7 @@ export type GetRecipesForEntityError = Fetcher.ErrorWrapper<{ export type GetRecipesForEntityResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -1069,7 +1167,7 @@ export type CreateRecipeError = Fetcher.ErrorWrapper<{ export type CreateRecipeResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -1128,7 +1226,7 @@ export type GetRecipeByIdError = Fetcher.ErrorWrapper<{ export type GetRecipeByIdResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -1296,6 +1394,78 @@ export const useRateRecipe = ( }); }; +export type GetBookmarkersPathParams = { + recipeId: number; +}; + +export type GetBookmarkersError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetBookmarkersResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.UserArray; +}; + +export type GetBookmarkersVariables = { + pathParams: GetBookmarkersPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetBookmarkers = ( + variables: GetBookmarkersVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetBookmarkersResponse, + GetBookmarkersError, + undefined, + {}, + {}, + GetBookmarkersPathParams + >({ + url: "/recipes/{recipeId}/bookmarks", + method: "get", + ...variables, + signal, + }); + +export const useGetBookmarkers = ( + variables: GetBookmarkersVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetBookmarkersResponse, + GetBookmarkersError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetBookmarkersResponse, + GetBookmarkersError, + TData + >({ + queryKey: queryKeyFn({ + path: "/recipes/{recipeId}/bookmarks", + operationId: "getBookmarkers", + variables, + }), + queryFn: ({ signal }) => + fetchGetBookmarkers({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + export type BookmarkRecipePathParams = { recipeId: number; }; @@ -1321,7 +1491,7 @@ export const fetchBookmarkRecipe = ( {}, BookmarkRecipePathParams >({ - url: "/recipes/{recipeId}/bookmark", + url: "/recipes/{recipeId}/bookmarks", method: "post", ...variables, signal, @@ -1349,6 +1519,330 @@ export const useBookmarkRecipe = ( }); }; +export type UnbookmarkRecipePathParams = { + recipeId: number; +}; + +export type UnbookmarkRecipeError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type UnbookmarkRecipeVariables = { + pathParams: UnbookmarkRecipePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchUnbookmarkRecipe = ( + variables: UnbookmarkRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + Responses.OkResponse, + UnbookmarkRecipeError, + undefined, + {}, + {}, + UnbookmarkRecipePathParams + >({ + url: "/recipes/{recipeId}/bookmarks", + method: "delete", + ...variables, + signal, + }); + +export const useUnbookmarkRecipe = ( + options?: Omit< + reactQuery.UseMutationOptions< + Responses.OkResponse, + UnbookmarkRecipeError, + UnbookmarkRecipeVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + Responses.OkResponse, + UnbookmarkRecipeError, + UnbookmarkRecipeVariables + >({ + mutationFn: (variables: UnbookmarkRecipeVariables) => + fetchUnbookmarkRecipe({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type GetCommentsForRecipePathParams = { + recipeId: number; +}; + +export type GetCommentsForRecipeError = Fetcher.ErrorWrapper<{ + status: 404; + payload: Responses.NotFoundResponse; +}>; + +export type GetCommentsForRecipeResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.CommentArray; +}; + +export type GetCommentsForRecipeVariables = { + pathParams: GetCommentsForRecipePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchGetCommentsForRecipe = ( + variables: GetCommentsForRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + GetCommentsForRecipeResponse, + GetCommentsForRecipeError, + undefined, + {}, + {}, + GetCommentsForRecipePathParams + >({ + url: "/recipes/{recipeId}/comments", + method: "get", + ...variables, + signal, + }); + +export const useGetCommentsForRecipe = ( + variables: GetCommentsForRecipeVariables, + options?: Omit< + reactQuery.UseQueryOptions< + GetCommentsForRecipeResponse, + GetCommentsForRecipeError, + TData + >, + "queryKey" | "queryFn" | "initialData" + >, +) => { + const { fetcherOptions, queryOptions, queryKeyFn } = + useSemanticBrowseContext(options); + return reactQuery.useQuery< + GetCommentsForRecipeResponse, + GetCommentsForRecipeError, + TData + >({ + queryKey: queryKeyFn({ + path: "/recipes/{recipeId}/comments", + operationId: "getCommentsForRecipe", + variables, + }), + queryFn: ({ signal }) => + fetchGetCommentsForRecipe({ ...fetcherOptions, ...variables }, signal), + ...options, + ...queryOptions, + }); +}; + +export type AddCommentToRecipePathParams = { + recipeId: number; +}; + +export type AddCommentToRecipeError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type AddCommentToRecipeResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.Comment; +}; + +export type AddCommentToRecipeRequestBody = { + comment?: string; +}; + +export type AddCommentToRecipeVariables = { + body?: AddCommentToRecipeRequestBody; + pathParams: AddCommentToRecipePathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchAddCommentToRecipe = ( + variables: AddCommentToRecipeVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + AddCommentToRecipeResponse, + AddCommentToRecipeError, + AddCommentToRecipeRequestBody, + {}, + {}, + AddCommentToRecipePathParams + >({ + url: "/recipes/{recipeId}/comments", + method: "post", + ...variables, + signal, + }); + +export const useAddCommentToRecipe = ( + options?: Omit< + reactQuery.UseMutationOptions< + AddCommentToRecipeResponse, + AddCommentToRecipeError, + AddCommentToRecipeVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + AddCommentToRecipeResponse, + AddCommentToRecipeError, + AddCommentToRecipeVariables + >({ + mutationFn: (variables: AddCommentToRecipeVariables) => + fetchAddCommentToRecipe({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type UpvoteCommentPathParams = { + recipeId: number; + commentId: number; +}; + +export type UpvoteCommentError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type UpvoteCommentResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.Comment; +}; + +export type UpvoteCommentVariables = { + pathParams: UpvoteCommentPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchUpvoteComment = ( + variables: UpvoteCommentVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + UpvoteCommentResponse, + UpvoteCommentError, + undefined, + {}, + {}, + UpvoteCommentPathParams + >({ + url: "/recipes/{recipeId}/comments/{commentId}/upvote", + method: "post", + ...variables, + signal, + }); + +export const useUpvoteComment = ( + options?: Omit< + reactQuery.UseMutationOptions< + UpvoteCommentResponse, + UpvoteCommentError, + UpvoteCommentVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + UpvoteCommentResponse, + UpvoteCommentError, + UpvoteCommentVariables + >({ + mutationFn: (variables: UpvoteCommentVariables) => + fetchUpvoteComment({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + +export type RemoveUpvoteFromCommentPathParams = { + recipeId: number; + commentId: number; +}; + +export type RemoveUpvoteFromCommentError = Fetcher.ErrorWrapper<{ + status: 400; + payload: Responses.BadRequestResponse; +}>; + +export type RemoveUpvoteFromCommentResponse = { + /** + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. + * + * @example 200 + * @example 201 + */ + status: 200 | 201; + data: Schemas.Comment; +}; + +export type RemoveUpvoteFromCommentVariables = { + pathParams: RemoveUpvoteFromCommentPathParams; +} & SemanticBrowseContext["fetcherOptions"]; + +export const fetchRemoveUpvoteFromComment = ( + variables: RemoveUpvoteFromCommentVariables, + signal?: AbortSignal, +) => + semanticBrowseFetch< + RemoveUpvoteFromCommentResponse, + RemoveUpvoteFromCommentError, + undefined, + {}, + {}, + RemoveUpvoteFromCommentPathParams + >({ + url: "/recipes/{recipeId}/comments/{commentId}/upvote", + method: "delete", + ...variables, + signal, + }); + +export const useRemoveUpvoteFromComment = ( + options?: Omit< + reactQuery.UseMutationOptions< + RemoveUpvoteFromCommentResponse, + RemoveUpvoteFromCommentError, + RemoveUpvoteFromCommentVariables + >, + "mutationFn" + >, +) => { + const { fetcherOptions } = useSemanticBrowseContext(); + return reactQuery.useMutation< + RemoveUpvoteFromCommentResponse, + RemoveUpvoteFromCommentError, + RemoveUpvoteFromCommentVariables + >({ + mutationFn: (variables: RemoveUpvoteFromCommentVariables) => + fetchRemoveUpvoteFromComment({ ...fetcherOptions, ...variables }), + ...options, + }); +}; + export type GetFeedQueryParams = { type: "explore" | "following"; }; @@ -1360,7 +1854,7 @@ export type GetFeedError = Fetcher.ErrorWrapper<{ export type GetFeedResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -1450,6 +1944,16 @@ export type QueryOperation = operationId: "getRecipeById"; variables: GetRecipeByIdVariables; } + | { + path: "/recipes/{recipeId}/bookmarks"; + operationId: "getBookmarkers"; + variables: GetBookmarkersVariables; + } + | { + path: "/recipes/{recipeId}/comments"; + operationId: "getCommentsForRecipe"; + variables: GetCommentsForRecipeVariables; + } | { path: "/feed"; operationId: "getFeed"; diff --git a/frontend/src/services/api/semanticBrowseResponses.ts b/frontend/src/services/api/semanticBrowseResponses.ts index c76ca975..6ce455af 100644 --- a/frontend/src/services/api/semanticBrowseResponses.ts +++ b/frontend/src/services/api/semanticBrowseResponses.ts @@ -12,7 +12,7 @@ export type OkResponse = Schemas.SuccessResponseObject; */ export type CreatedResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -26,7 +26,7 @@ export type CreatedResponse = { */ export type BadRequestResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 @@ -44,7 +44,7 @@ export type BadRequestResponse = { */ export type UnauthorizedResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 @@ -62,7 +62,7 @@ export type UnauthorizedResponse = { */ export type ForbiddenResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 @@ -80,7 +80,7 @@ export type ForbiddenResponse = { */ export type NotFoundResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 @@ -98,7 +98,7 @@ export type NotFoundResponse = { */ export type ConflictResponse = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 diff --git a/frontend/src/services/api/semanticBrowseSchemas.ts b/frontend/src/services/api/semanticBrowseSchemas.ts index 27b350ca..bb3896d0 100644 --- a/frontend/src/services/api/semanticBrowseSchemas.ts +++ b/frontend/src/services/api/semanticBrowseSchemas.ts @@ -39,7 +39,7 @@ export type UserProfile = { diets?: string[]; recipeCount?: number; /** - * Only available when querying own profile. + * Only available when querying the current user's profile. */ bookmarks?: RecipeSummary[]; recipes?: RecipeSummary[]; @@ -49,19 +49,19 @@ export type UserProfile = { * @example {"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","followersCount":100,"profilePicture":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","recipeCount":10,"avgRating":4.5} */ export type UserSummary = { - id?: number; - username?: string; - name?: string; - followersCount?: number; + id: number; + username: string; + name: string; + followersCount: number; /** * @format uri */ - profilePicture?: string; - recipeCount?: number; + profilePicture: string; + recipeCount: number; /** * @format float */ - avgRating?: number; + avgRating: number; }; /** @@ -93,11 +93,12 @@ export type CuisineDetails = { * @format uri */ image: string; + isSelfFollowing?: boolean; dishes?: DishSummary[]; }; /** - * @example {"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","cookTime":"30 minutes","images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"rating":4.5,"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki","image":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"},"author":{"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","followersCount":100,"profilePicture":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","recipeCount":10,"avgRating":4.5}} + * @example {"id":1,"name":"My Takoyaki Recipe","description":"A delicious takoyaki recipe that I learned from my grandmother.","cookTime":30,"images":["http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"],"avgRating":4.5,"ratingsCount":42,"selfRating":5,"dish":{"id":"http://www.wikidata.org/entity/Q905527","name":"takoyaki","image":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg"},"author":{"id":1,"username":"takoyaki_lover","name":"Takoyaki Lover","followersCount":100,"profilePicture":"http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg","recipeCount":10,"avgRating":4.5}} */ export type RecipeSummary = { id: number; @@ -106,12 +107,14 @@ export type RecipeSummary = { /** * Cook time in minutes */ - cookTime?: number; + cookTime: number; images: string[]; /** * @format float */ - rating: number; + avgRating: number; + ratingsCount: number; + selfRating?: number; dish?: DishSummary; author: UserSummary; }; @@ -136,6 +139,10 @@ export type RecipeDetails = { */ avgRating?: number; ratingsCount: number; + /** + * The current user's rating for this recipe, if any. + */ + selfRating?: number; author: UserSummary; }; @@ -171,6 +178,39 @@ export type DishSummary = { name: string; }; +export type Comment = { + id: number; + author: UserSummary; + recipeId?: number; + upvoteCount: number; + content: string; + hasSelfUpvoted: boolean; + /** + * @format date-time + */ + createdAt: string; +}; + +/** + * An array of comments + */ +export type CommentArray = Comment[]; + +/** + * An array of dishes + */ +export type DishArray = DishDetails[]; + +/** + * An array of recipes + */ +export type RecipeArray = RecipeDetails[]; + +/** + * An array of users + */ +export type UserArray = UserSummary[]; + /** * @example {"status":200,"data":{"message":"Success"}} * @example {"status":400,"errors":[{"message":"Invalid email","field":"email"},{"message":"Invalid password","field":"password"}]} @@ -190,7 +230,7 @@ export type ApiError = { */ export type SuccessResponseObject = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 200 * @example 201 @@ -204,7 +244,7 @@ export type SuccessResponseObject = { */ export type ErrorResponseObject = { /** - * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritive the inner status over the HTTP status. + * Internal status code of the response. An HTTP 200 response with an internal 500 status code is an error response. Prioritize the inner status over the HTTP status. * * @example 400 * @example 401 @@ -216,18 +256,3 @@ export type ErrorResponseObject = { status: 400 | 401 | 403 | 404 | 409 | 500; errors: ApiError[]; }; - -/** - * An array of dishes - */ -export type DishArray = DishDetails[]; - -/** - * An array of recipes - */ -export type RecipeArray = RecipeDetails[]; - -/** - * An array of users - */ -export type UserArray = UserSummary[]; diff --git a/swagger/openapi.yml b/swagger/openapi.yml index be9904f0..2e34cb81 100644 --- a/swagger/openapi.yml +++ b/swagger/openapi.yml @@ -3,7 +3,7 @@ info: title: Semantic Cuisine description: |- This is the API specification for the Semantic Browse for Cuisines application. - version: 1.0.2 + version: 1.0.3 externalDocs: description: Refer to the requirements url: https://github.com/bounswe/bounswe2024group1/wiki/Requirements @@ -256,6 +256,7 @@ paths: tags: - user summary: Update user profile + description: Can only update own profile security: - auth_jwt: [] parameters: @@ -285,6 +286,27 @@ paths: "404": $ref: "#/components/responses/NotFoundResponse" + /users/me: + get: + operationId: getMe + tags: + - user + summary: Get own profile + security: + - auth_jwt: [] + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/UserProfile" + /users/{userId}/following: get: operationId: getUserFollowing @@ -341,7 +363,7 @@ paths: "400": $ref: "#/components/responses/BadRequestResponse" - /users/follow: + /users/{userId}/follow: post: operationId: followUser tags: @@ -349,43 +371,53 @@ paths: summary: Follow a user security: - auth_jwt: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - followingUserId: - type: integer + parameters: + - in: path + name: userId + required: true + schema: + type: integer responses: "200": - $ref: "#/components/responses/OkResponse" + description: OK - returning new user profile + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/UserProfile" "400": $ref: "#/components/responses/BadRequestResponse" "404": $ref: "#/components/responses/NotFoundResponse" - - /users/unfollow: - post: + delete: operationId: unfollowUser tags: - user summary: Unfollow a user security: - auth_jwt: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - followingUserId: - type: integer + parameters: + - in: path + name: userId + required: true + schema: + type: integer responses: "200": - $ref: "#/components/responses/OkResponse" + description: OK - returning new user profile + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/UserProfile" "400": $ref: "#/components/responses/BadRequestResponse" "404": @@ -510,6 +542,36 @@ paths: $ref: "#/components/schemas/CuisineDetails" "404": $ref: "#/components/responses/NotFoundResponse" + /cuisines/{cuisineId}/follow: + post: + operationId: followCuisine + tags: + - cuisines + summary: Follow a cuisine + security: + - auth_jwt: [] + parameters: + - in: path + name: cuisineId + required: true + schema: + type: integer + responses: + "200": + description: OK - returning cuisine details + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/CuisineDetails" + "400": + $ref: "#/components/responses/BadRequestResponse" + "404": + $ref: "#/components/responses/NotFoundResponse" /recipes: get: @@ -654,7 +716,32 @@ paths: "400": $ref: "#/components/responses/BadRequestResponse" - /recipes/{recipeId}/bookmark: + /recipes/{recipeId}/bookmarks: + get: + operationId: getBookmarkers + tags: + - recipe + summary: Get users who bookmarked a recipe + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/UserArray" + "404": + $ref: "#/components/responses/NotFoundResponse" post: operationId: bookmarkRecipe tags: @@ -673,6 +760,153 @@ paths: $ref: "#/components/responses/OkResponse" "400": $ref: "#/components/responses/BadRequestResponse" + delete: + operationId: unbookmarkRecipe + tags: + - recipe + summary: Unbookmark a recipe + security: + - auth_jwt: [] + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + responses: + "204": + description: OK + $ref: "#/components/responses/OkResponse" + "400": + $ref: "#/components/responses/BadRequestResponse" + /recipes/{recipeId}/comments: + get: + operationId: getCommentsForRecipe + tags: + - recipe + summary: Get comments for a recipe + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/CommentArray" + "404": + $ref: "#/components/responses/NotFoundResponse" + post: + operationId: addCommentToRecipe + tags: + - recipe + summary: Add a comment to a recipe + security: + - auth_jwt: [] + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + comment: + type: string + responses: + "201": + description: Created + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/Comment" + "400": + $ref: "#/components/responses/BadRequestResponse" + + /recipes/{recipeId}/comments/{commentId}/upvote: + post: + operationId: upvoteComment + tags: + - recipe + summary: Upvote a comment + security: + - auth_jwt: [] + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + - in: path + name: commentId + required: true + schema: + type: integer + responses: + "200": + description: OK - returns new comment after upvote + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/Comment" + "400": + $ref: "#/components/responses/BadRequestResponse" + delete: + operationId: removeUpvoteFromComment + tags: + - recipe + summary: Remove upvote from a comment + security: + - auth_jwt: [] + parameters: + - in: path + name: recipeId + required: true + schema: + type: integer + - in: path + name: commentId + required: true + schema: + type: integer + responses: + "200": + description: OK - returns new comment after upvote removal + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/SuccessResponseObject" + - type: object + properties: + data: + $ref: "#/components/schemas/Comment" + "400": + $ref: "#/components/responses/BadRequestResponse" /feed: get: @@ -775,7 +1009,7 @@ components: type: integer bookmarks: type: array - description: Only available when querying own profile. + description: Only available when querying the current user's profile. items: $ref: "#/components/schemas/RecipeSummary" recipes: @@ -816,6 +1050,14 @@ components: name: "takoyaki" UserSummary: type: object + required: + - id + - username + - name + - followersCount + - profilePicture + - recipeCount + - avgRating properties: id: type: integer @@ -900,6 +1142,8 @@ components: image: type: string format: uri + isSelfFollowing: + type: boolean dishes: type: array items: @@ -913,6 +1157,9 @@ components: - description - images - rating + - cookTime + - avgRating + - ratingsCount - author properties: id: @@ -929,9 +1176,13 @@ components: items: type: string format: uri - rating: + avgRating: type: number format: float + ratingsCount: + type: integer + selfRating: + type: integer dish: $ref: "#/components/schemas/DishSummary" author: @@ -940,10 +1191,12 @@ components: - id: 1 name: "My Takoyaki Recipe" description: "A delicious takoyaki recipe that I learned from my grandmother." - cookTime: "30 minutes" + cookTime: 30 images: - "http://commons.wikimedia.org/wiki/Special:FilePath/Takoyaki%20by%20yomi955.jpg" - rating: 4.5 + avgRating: 4.5 + ratingsCount: 42 + selfRating: 5 dish: id: "http://www.wikidata.org/entity/Q905527" name: "takoyaki" @@ -1008,6 +1261,9 @@ components: format: float ratingsCount: type: integer + selfRating: + type: integer + description: The current user's rating for this recipe, if any. author: $ref: "#/components/schemas/UserSummary" examples: @@ -1127,6 +1383,56 @@ components: - id: 1 name: "takoyaki" + Comment: + type: object + required: + - id + - author + - content + - upvoteCount + - hasSelfUpvoted + - createdAt + properties: + id: + type: integer + author: + $ref: "#/components/schemas/UserSummary" + recipeId: + type: integer + upvoteCount: + type: integer + content: + type: string + hasSelfUpvoted: + type: boolean + createdAt: + type: string + format: date-time + + CommentArray: + description: An array of comments + type: array + items: + $ref: "#/components/schemas/Comment" + + DishArray: + description: An array of dishes + type: array + items: + $ref: "#/components/schemas/DishDetails" + + RecipeArray: + description: An array of recipes + type: array + items: + $ref: "#/components/schemas/RecipeDetails" + + UserArray: + description: An array of users + type: array + items: + $ref: "#/components/schemas/UserSummary" + ApiResponse: oneOf: - $ref: "#/components/schemas/SuccessResponseObject" @@ -1160,7 +1466,7 @@ components: type: integer description: Internal status code of the response. An HTTP 200 response with an internal - 500 status code is an error response. Prioritive the inner status over the HTTP + 500 status code is an error response. Prioritize the inner status over the HTTP status. enum: - 200 @@ -1185,28 +1491,13 @@ components: - 500 description: Internal status code of the response. An HTTP 200 response with an internal - 500 status code is an error response. Prioritive the inner status over the HTTP + 500 status code is an error response. Prioritize the inner status over the HTTP status. examples: [400, 401, 403, 404, 409, 500] errors: type: array items: $ref: "#/components/schemas/ApiError" - DishArray: - description: An array of dishes - type: array - items: - $ref: "#/components/schemas/DishDetails" - RecipeArray: - description: An array of recipes - type: array - items: - $ref: "#/components/schemas/RecipeDetails" - UserArray: - description: An array of users - type: array - items: - $ref: "#/components/schemas/UserSummary" responses: OkResponse: description: OK From f84c4982d275e3dee039aa9bc838807cfcbc400a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Wed, 8 May 2024 16:56:48 +0300 Subject: [PATCH 22/74] feature(frontend): add react-hook-form to login and signup forms Add error handler compatible with our API spec. Migrated login and signup forms to react-hook-form. Removed react-query-swagger which was not used. --- frontend/.eslintrc.cjs | 1 + frontend/package.json | 16 +- frontend/src/components/ui/form.tsx | 177 +++++++++++ frontend/src/routes/index.tsx | 6 +- frontend/src/routes/login.tsx | 178 +++++------ frontend/src/routes/signup.tsx | 289 +++++++++++------- .../api/semanticBrowseFetcher.test.ts | 88 ++++++ .../src/services/api/semanticBrowseFetcher.ts | 25 +- frontend/yarn.lock | 69 +++-- 9 files changed, 604 insertions(+), 245 deletions(-) create mode 100644 frontend/src/components/ui/form.tsx diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 6aac01dd..723d6cb3 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -22,5 +22,6 @@ module.exports = { "off", { allowConstantExport: true }, ], + "@typescript-eslint/no-unused-vars": ["warn"], }, }; diff --git a/frontend/package.json b/frontend/package.json index 61f7b07c..698b3966 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,8 @@ "generate:api": "openapi-codegen gen semanticBrowse" }, "dependencies": { + "@hookform/error-message": "^2.0.1", + "@hookform/resolvers": "^3.3.4", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", @@ -32,10 +34,11 @@ "lucide-react": "^0.376.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.51.4", "react-router-dom": "^6.23.0", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.5", + "zod": "^3.23.7", "zustand": "^4.5.2" }, "devDependencies": { @@ -62,7 +65,6 @@ "postcss": "^8.4.38", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.14", - "react-query-swagger": "^15.11.1", "tailwindcss": "^3.4.3", "typescript": "^5.2.2", "vite": "^5.2.0", @@ -72,8 +74,14 @@ "pre-commit": "lint-staged && tsc" }, "lint-staged": { - "*/**/*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix", "eslint"], - "*/**/*.{json,css,md}": ["prettier --write"] + "*/**/*.{js,jsx,ts,tsx}": [ + "prettier --write", + "eslint --fix", + "eslint" + ], + "*/**/*.{json,css,md}": [ + "prettier --write" + ] }, "packageManager": "yarn@4.1.1" } diff --git a/frontend/src/components/ui/form.tsx b/frontend/src/components/ui/form.tsx new file mode 100644 index 00000000..497718a9 --- /dev/null +++ b/frontend/src/components/ui/form.tsx @@ -0,0 +1,177 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +