diff --git a/.github/workflows/frontend-ci-development.yml b/.github/workflows/frontend-ci-development.yml index f2f49f12..bb941106 100644 --- a/.github/workflows/frontend-ci-development.yml +++ b/.github/workflows/frontend-ci-development.yml @@ -43,18 +43,20 @@ jobs: - name: stylelint 실행 run: yarn lint:styled - - name: 테스트 실행 + - name: hook 테스트 실행 run: yarn test - name: 테스트 결과 PR에 코멘트 등록 uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: - files: frontend/test-results/results.xml + files: | + frontend/test-results/results.xml - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트 등록 uses: mikepenz/action-junit-report@v3 if: always() with: - report_paths: frontend/test-results/results.xml + report_paths: | + frontend/test-results/results.xml token: ${{ secrets.GITHUB_TOKEN }} diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index a755d3ac..7ec885e7 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -7,6 +7,7 @@ module.exports = { "plugin:compat/recommended", "plugin:storybook/recommended", "plugin:jest/recommended", + "plugin:cypress/recommended", ], plugins: ["react-refresh", "prettier", "@typescript-eslint"], ignorePatterns: ["dist", ".eslintrc.cjs"], diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts new file mode 100644 index 00000000..37aec732 --- /dev/null +++ b/frontend/cypress.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + baseUrl: "http://localhost:3000", + viewportWidth: 480, + viewportHeight: 1000, + }, +}); diff --git a/frontend/cypress/cypress.d.ts b/frontend/cypress/cypress.d.ts new file mode 100644 index 00000000..f37eda45 --- /dev/null +++ b/frontend/cypress/cypress.d.ts @@ -0,0 +1,9 @@ +declare namespace Cypress { + interface Chainable { + simulateKakaoLogin(): Chainable; + fillTravelPlanBasicInfo(title: string, date: number): Chainable; + addPlace(placeName: string): Chainable; + addTodo(todoText: string): Chainable; + submitTravelPlan(): Chainable; + } +} diff --git a/frontend/cypress/e2e/login.cy.ts b/frontend/cypress/e2e/login.cy.ts new file mode 100644 index 00000000..02bc4d86 --- /dev/null +++ b/frontend/cypress/e2e/login.cy.ts @@ -0,0 +1,5 @@ +describe("카카오 로그인 e2e 테스트", () => { + it("카카오 로그인이 완료되면 메인 페이지로 이동된다.", () => { + cy.simulateKakaoLogin(); + }); +}); diff --git a/frontend/cypress/e2e/mainPage.cy.ts b/frontend/cypress/e2e/mainPage.cy.ts new file mode 100644 index 00000000..c629be23 --- /dev/null +++ b/frontend/cypress/e2e/mainPage.cy.ts @@ -0,0 +1,83 @@ +import travelogueData from "@mocks/data/travelogue.json"; + +import { CYPRESS_SELECTOR_MAP } from "@constants/cypress"; +import { ROUTE_PATHS_MAP } from "@constants/route"; + +describe("메인 페이지 테스트", () => { + beforeEach(() => { + cy.intercept("GET", `${Cypress.env("BASE_URL")}travelogues*`, (req) => { + const page = (req.query.page === "0" ? 1 : req.query.page) as number; + const pageSize = 5; + const start = (page - 1) * pageSize; + const end = start + pageSize; + + const paginatedData = travelogueData.slice(start, end); + + req.reply({ + statusCode: 200, + body: { + content: paginatedData, + }, + }); + }).as("getTravelogues"); + + cy.visit(ROUTE_PATHS_MAP.root); + }); + + describe("여행기 무한 스크롤 테스트", () => { + it("메인 페이지로 진입했을때 보여지는 여행기는 총 5개이다.", () => { + // when + cy.wait("@getTravelogues").then(() => { + // then + cy.get(CYPRESS_SELECTOR_MAP.main.travelogueItem, { timeout: 10000 }).should( + "have.length", + 5, + ); + }); + }); + + it("스크롤을 내릴 경우 보여지는 여행기는 총 10개이다.", () => { + // when + cy.wait("@getTravelogues"); + cy.scrollTo("bottom"); + cy.wait("@getTravelogues"); + + // then + cy.get(CYPRESS_SELECTOR_MAP.main.travelogueItem).should("have.length", 10); + }); + }); + + describe("태그 관련 테스트", () => { + const SELECTED_CHIP_SELECTOR = '[data-cy="selected-chip"]'; + + it("태그 선택 및 해제가 가능해야한다.", () => { + // when + cy.contains("여름").click(); + + // then + cy.get(SELECTED_CHIP_SELECTOR).should("have.length", 1); + + // when + cy.contains("여름").click(); + + // then + cy.get(SELECTED_CHIP_SELECTOR).should("have.length", 0); + }); + + it("최대 태그 선택은 3개까지 가능하다.", () => { + // when + cy.contains("여름").click(); + cy.contains("가족").click(); + cy.contains("도보").click(); + + // then + cy.get(SELECTED_CHIP_SELECTOR).should("have.length", 3); + + // when + cy.contains("맛집").click(); + + // then + cy.get(SELECTED_CHIP_SELECTOR).should("have.length", 3); + }); + }); +}); diff --git a/frontend/cypress/e2e/travelPlanRegister.cy.ts b/frontend/cypress/e2e/travelPlanRegister.cy.ts new file mode 100644 index 00000000..c7e04457 --- /dev/null +++ b/frontend/cypress/e2e/travelPlanRegister.cy.ts @@ -0,0 +1,273 @@ +/* eslint-disable jest/expect-expect */ +import { CYPRESS_SELECTOR_MAP } from "@constants/cypress"; +import { ROUTE_PATHS_MAP } from "@constants/route"; + +describe("여행 계획 등록 테스트", () => { + beforeEach(() => { + cy.simulateKakaoLogin(); + cy.visit(ROUTE_PATHS_MAP.travelPlanRegister); + }); + + describe("제목 입력 테스트", () => { + it("제목을 입력할 수 있다", () => { + // given + const INPUT_VALUE = "테스트 여행 계획"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).type(INPUT_VALUE); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).should("have.value", INPUT_VALUE); + }); + + it("제목을 17자 입력한 경우 총 제목 글자수가 17자이다.", () => { + // given + const INPUT_VALUE = "지니, 리버, 시모의 여행 계획"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).type(INPUT_VALUE); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).should("have.value", INPUT_VALUE); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput) + .invoke("val") + .should("have.length", 17); + }); + + it("제목을 21자 입력한 경우 20자만 보여진다.", () => { + // given + const INPUT_VALUE = "지니, 리버, 시모의 여행 계획 세우기"; + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput); + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).type(INPUT_VALUE); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).should( + "have.value", + INPUT_VALUE.slice(0, 20), + ); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput) + .invoke("val") + .should("have.length", 20); + }); + }); + + describe("시작 날짜 입력 테스트", () => { + function formatDate(date) { + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${year}. ${month}. ${day}`; + } + + it("당일에 해당하는 시작일을 입력할 수 있다", () => { + // given + const today = new Date(); + const formattedDate = formatDate(today); + const todayDate = today.getDate().toString(); + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).click(); + cy.contains(CYPRESS_SELECTOR_MAP.calendar.dayCell, todayDate).click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).should( + "have.value", + formattedDate, + ); + }); + + it("시작일은 지난 날짜를 추가할 수 없다", () => { + // given + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayDate = yesterday.getDate().toString(); + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).click(); + cy.contains(CYPRESS_SELECTOR_MAP.calendar.dayCell, yesterdayDate).click({ + force: true, + }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).should("have.value", ""); + }); + + it("이전 달 버튼은 disable 처리되어 있어야한다.", () => { + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).click(); + + // then + cy.get(CYPRESS_SELECTOR_MAP.calendar.previousMonthMoveButton) + .should("be.visible") + .and("be.disabled"); + }); + + it("다음 달 버튼을 클릭하면 다음 달에 해당되는 캘린더들이 보여져야한다.", () => { + // given + const today = new Date(); + const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1); + const lastDayOfNextMonth = new Date( + nextMonth.getFullYear(), + nextMonth.getMonth() + 1, + 0, + ).getDate(); + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).click(); + cy.get(CYPRESS_SELECTOR_MAP.calendar.nextMonthMoveButton).click(); + + // then + cy.get(CYPRESS_SELECTOR_MAP.calendar.headTitle).should("be.visible"); + cy.get(CYPRESS_SELECTOR_MAP.calendar.dayCell).contains("1").should("be.visible"); + cy.get(CYPRESS_SELECTOR_MAP.calendar.dayCell) + .contains(lastDayOfNextMonth.toString()) + .should("be.visible"); + cy.get(CYPRESS_SELECTOR_MAP.calendar.previousMonthMoveButton).should("be.enabled"); + }); + }); + + describe("여행 일자 아코디언 관련 테스트", () => { + it("일자 추가 버튼을 눌러 다음 여행 날짜를 추가할 수 있다.", () => { + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + + // then + cy.get(CYPRESS_SELECTOR_MAP.accordion.item).should("exist").should("have.length", 1); + }); + + it("일자 삭제 버튼을 눌러 해당 일자를 삭제할 수 있다.", () => { + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + + // then + cy.get(CYPRESS_SELECTOR_MAP.accordion.item).should("exist"); + + // when + cy.get(CYPRESS_SELECTOR_MAP.accordion.trigger.deleteButton).click(); + + // then + cy.get(CYPRESS_SELECTOR_MAP.accordion.item).should("not.exist"); + }); + + it("장소 추가하기 버튼을 누르고 장소를 추가할 수 있다.", () => { + // given + const INPUT_VALUE = "도쿄"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addPlaceButton).click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.container).should("exist"); + + // when + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(INPUT_VALUE); + cy.get(".pac-item").first().click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.container).should("not.exist"); + cy.get(CYPRESS_SELECTOR_MAP.accordion.item).should("contain", INPUT_VALUE); + }); + + it("특정 장소를 제거할 수 있다", () => { + // given + const INPUT_VALUE = "도쿄"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addPlaceButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(INPUT_VALUE); + cy.get(".pac-item").first().click(); + + cy.contains(CYPRESS_SELECTOR_MAP.accordion.item, INPUT_VALUE) + .find(CYPRESS_SELECTOR_MAP.accordion.trigger.deleteButton) + .click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.accordion.item).should("not.contain", INPUT_VALUE); + }); + + it("장소 내 TO DO를 추가할 수 있다", () => { + // given + const INPUT_VALUE = "도쿄"; + const TODO_TEXT = "스시 먹기"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addPlaceButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(INPUT_VALUE); + cy.get(".pac-item").first().click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addTodoButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.todoInput).type(TODO_TEXT); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.todoInput).should("have.value", TODO_TEXT); + }); + + it("장소 내 TO DO를 삭제할 수 있다", () => { + // given + const INPUT_VALUE = "도쿄"; + + // when + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addPlaceButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(INPUT_VALUE); + cy.get(".pac-item").first().click(); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addTodoButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.deleteTodoButton).click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.todoInput).should("not.exist"); + }); + }); + + describe("여행 계획 등록 관련 테스트", () => { + it("등록 버튼을 누르면 여행 계획 등록이 가능하다.", () => { + // given + cy.intercept("POST", `${Cypress.env("BASE_URL")}/travel-plans`, { + statusCode: 201, + }).as("travelPlanRegisterRequest"); + + const TITLE_INPUT_VALUE = "도쿄 여행"; + const TODAY_DATE = new Date().getDate(); + const PLACE_INPUT_VALUE = "도쿄 타워"; + const TODO_VALUE = "도쿄 타워 구경가기"; + + // when + cy.fillTravelPlanBasicInfo(TITLE_INPUT_VALUE, TODAY_DATE); + cy.addPlace(PLACE_INPUT_VALUE); + cy.addTodo(TODO_VALUE); + cy.submitTravelPlan(); + + // then + cy.wait("@travelPlanRegisterRequest").then((interception) => { + assert.equal(interception.response.statusCode, 201); + }); + }); + + it("취소 버튼을 누르면 여행 계획 등록이 취소된다.", () => { + // given + const TITLE_INPUT_VALUE = "도쿄 여행"; + const TODAY_DATE = new Date().getDate(); + const PLACE_INPUT_VALUE = "도쿄 타워"; + const TODO_VALUE = "도쿄 타워 구경가기"; + + // when + cy.fillTravelPlanBasicInfo(TITLE_INPUT_VALUE, TODAY_DATE); + cy.addPlace(PLACE_INPUT_VALUE); + cy.addTodo(TODO_VALUE); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.registerButton).click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.modalBottomSheet.container).should("exist"); + + // when + cy.get(CYPRESS_SELECTOR_MAP.modalBottomSheet.closeButton).click({ force: true }); + + // then + cy.get(CYPRESS_SELECTOR_MAP.modalBottomSheet.container).should("not.exist"); + }); + }); +}); diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts new file mode 100644 index 00000000..f45b3332 --- /dev/null +++ b/frontend/cypress/support/commands.ts @@ -0,0 +1,50 @@ +import { CYPRESS_SELECTOR_MAP } from "../../src/constants/cypress"; +import { ROUTE_PATHS_MAP } from "../../src/constants/route"; + +Cypress.Commands.add("simulateKakaoLogin", () => { + const mockCode = "mock_auth_code"; + const redirectUri = encodeURIComponent( + `${Cypress.config().baseUrl}${ROUTE_PATHS_MAP.loginCallback}`, + ); + + cy.intercept("POST", "**/login/oauth/kakao*", (req) => { + req.reply({ + statusCode: 200, + body: { + accessToken: Cypress.env("ACCESS_TOKEN"), + refreshToken: Cypress.env("REFRESH_TOKEN"), + memberId: Cypress.env("MEMBER_ID"), + }, + }); + }).as("loginOauthRequest"); + + cy.visit(`${ROUTE_PATHS_MAP.loginCallback}?code=${mockCode}&redirectUri=${redirectUri}`); + + cy.wait("@loginOauthRequest"); + + cy.url().should("eq", `${Cypress.config().baseUrl}${ROUTE_PATHS_MAP.root}`); +}); + +Cypress.Commands.add("fillTravelPlanBasicInfo", (title, date) => { + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.titleInput).type(title); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.startDateInput).click({ force: true }); + cy.contains(CYPRESS_SELECTOR_MAP.calendar.dayCell, date).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addDateButton).click(); +}); + +Cypress.Commands.add("addPlace", (placeName) => { + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addPlaceButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(placeName); + cy.get(".pac-item").first().click(); +}); + +Cypress.Commands.add("addTodo", (todoText) => { + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.addTodoButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.todoInput).type(todoText); +}); + +Cypress.Commands.add("submitTravelPlan", () => { + cy.get(CYPRESS_SELECTOR_MAP.travelPlanRegister.registerButton).click({ force: true }); + cy.get(CYPRESS_SELECTOR_MAP.modalBottomSheet.container).should("exist"); + cy.get(CYPRESS_SELECTOR_MAP.modalBottomSheet.confirmButton).click({ force: true }); +}); diff --git a/frontend/cypress/support/e2e.ts b/frontend/cypress/support/e2e.ts new file mode 100644 index 00000000..f80f74f8 --- /dev/null +++ b/frontend/cypress/support/e2e.ts @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') \ No newline at end of file diff --git a/frontend/cypress/tsconfig.json b/frontend/cypress/tsconfig.json new file mode 100644 index 00000000..19a62766 --- /dev/null +++ b/frontend/cypress/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2020", + "lib": ["es2020", "dom"], + "types": ["cypress", "node"], + "module": "NodeNext", + "moduleResolution": "nodenext", + "baseUrl": "../src", + "paths": { + "@styles/*": ["styles/*"], + "@assets/*": ["assets/*"], + "@components/*": ["components/*"], + "@apis/*": ["apis/*"], + "@mocks/*": ["mocks/*"], + "@constants/*": ["constants/*"], + "@type/*": ["types/*"], + "@queries/*": ["queries/*"], + "@hooks/*": ["hooks/*"], + "@contexts/*": ["contexts/*"], + "@utils/*": ["utils/*"] + }, + "resolveJsonModule": true + }, + "include": ["**/*.ts"] +} diff --git a/frontend/package.json b/frontend/package.json index eb0c8aab..649e064a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "2024-touroot", - "version": "1.0.1", + "version": "1.1.0", "description": "to your route, 투룻!", "repository": "https://github.com/woowacourse-teams/2024-touroot", "scripts": { @@ -10,6 +10,8 @@ "build:production": "NODE_ENV=production webpack --config webpack.production.js", "lint:styled": "stylelint './src/**/*.styled.ts' --fix", "test": "jest --passWithNoTests", + "test-e2e": "cypress open", + "test-e2e:run": "cypress run --browser chrome", "storybook": "storybook dev -p 6006", "lint": "eslint src/", "build-storybook": "storybook build" @@ -23,10 +25,12 @@ "@tanstack/react-query-devtools": "^5.51.1", "axios": "^1.7.2", "dotenv-webpack": "^8.1.0", + "immer": "^10.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-ga4": "^2.1.0", - "react-router-dom": "^6.25.1" + "react-router-dom": "^6.25.1", + "use-immer": "^0.10.0" }, "devDependencies": { "@babel/core": "^7.25.2", @@ -57,9 +61,11 @@ "@typescript-eslint/parser": "^7.16.0", "babel-loader": "^9.1.3", "css-loader": "^7.1.2", + "cypress": "^13.13.3", "eslint": "8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-compat": "^5.0.0", + "eslint-plugin-cypress": "^3.5.0", "eslint-plugin-jest": "^28.6.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.3", @@ -86,6 +92,7 @@ "typescript": "^5.5.3", "undici": "^6.19.2", "webpack": "^5.92.1", + "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", "webpack-merge": "^6.0.1" diff --git a/frontend/public/index.html b/frontend/public/index.html index fa424b9b..59394dc2 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -2,19 +2,25 @@ - + + + + + + + to your route, 투룻 + + - -
diff --git a/frontend/src/components/common/Accordion/AccordionItem/AccordionItem.tsx b/frontend/src/components/common/Accordion/AccordionItem/AccordionItem.tsx index 8cad10c8..6d84588c 100644 --- a/frontend/src/components/common/Accordion/AccordionItem/AccordionItem.tsx +++ b/frontend/src/components/common/Accordion/AccordionItem/AccordionItem.tsx @@ -1,5 +1,7 @@ import { AccordionItemContext } from "@components/common/Accordion/AccordionItem/accordionItemContext"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import * as S from "./AccordionItem.styled"; interface AccordionItemProps extends React.PropsWithChildren { @@ -9,7 +11,7 @@ interface AccordionItemProps extends React.PropsWithChildren { const AccordionItem = ({ value, children }: AccordionItemProps) => { return ( - {children} + {children} ); }; diff --git a/frontend/src/components/common/Accordion/AccordionRoot/AccordionRoot.styled.ts b/frontend/src/components/common/Accordion/AccordionRoot/AccordionRoot.styled.ts index 716164d3..7309468d 100644 --- a/frontend/src/components/common/Accordion/AccordionRoot/AccordionRoot.styled.ts +++ b/frontend/src/components/common/Accordion/AccordionRoot/AccordionRoot.styled.ts @@ -3,5 +3,5 @@ import styled from "@emotion/styled"; export const Layout = styled.div` display: flex; flex-direction: column; - gap: 2rem; + gap: ${({ theme }) => theme.spacing.m}; `; diff --git a/frontend/src/components/common/Accordion/AccordionTrigger/AccordionTrigger.tsx b/frontend/src/components/common/Accordion/AccordionTrigger/AccordionTrigger.tsx index 08204a05..28367192 100644 --- a/frontend/src/components/common/Accordion/AccordionTrigger/AccordionTrigger.tsx +++ b/frontend/src/components/common/Accordion/AccordionTrigger/AccordionTrigger.tsx @@ -2,6 +2,8 @@ import { useAccordionItemContext } from "@components/common/Accordion/AccordionI import { useAccordionContext } from "@components/common/Accordion/AccordionRoot/accordionContext"; import IconButton from "@components/common/IconButton/IconButton"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import { DownArrow, UpArrow } from "@assets/svg"; import * as S from "./AccordionTrigger.styled"; @@ -23,7 +25,13 @@ const AccordionTrigger = ({ children, onDeleteItem }: AccordionTriggerProps) => {children} - + ); }; diff --git a/frontend/src/components/common/Calendar/Calendar.styled.ts b/frontend/src/components/common/Calendar/Calendar.styled.ts index 48fa6056..64042570 100644 --- a/frontend/src/components/common/Calendar/Calendar.styled.ts +++ b/frontend/src/components/common/Calendar/Calendar.styled.ts @@ -68,8 +68,8 @@ export const DayCell = styled.td<{ $isCurrentMonth: boolean; $isSelectable: bool } `}; - color: ${({ theme, $isCurrentMonth }) => - $isCurrentMonth ? theme.colors.text.secondary : PRIMITIVE_COLORS.gray[300]}; + color: ${({ theme, $isCurrentMonth, $isSelectable }) => + $isCurrentMonth && $isSelectable ? theme.colors.text.secondary : PRIMITIVE_COLORS.gray[300]}; text-align: center; `; diff --git a/frontend/src/components/common/Calendar/Calendar.tsx b/frontend/src/components/common/Calendar/Calendar.tsx index a2538261..85d7db80 100644 --- a/frontend/src/components/common/Calendar/Calendar.tsx +++ b/frontend/src/components/common/Calendar/Calendar.tsx @@ -6,6 +6,8 @@ import Text from "@components/common/Text/Text"; import useCalendar from "@hooks/useCalendar"; import useClickAway from "@hooks/useClickAway"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import { PRIMITIVE_COLORS } from "@styles/tokens"; import * as S from "./Calendar.styled"; @@ -42,8 +44,9 @@ const Calendar = ({ iconType="prev-arrow" onClick={prevMonth} disabled={today.getMonth() === calendarDetail.month} + data-cy={CYPRESS_DATA_MAP.calendar.previousMonthMoveButton} /> - + {calendarDetail.year}년 {calendarDetail.month + 1}월 @@ -69,8 +73,9 @@ const Calendar = ({ $isCurrentMonth={isCurrentMonth} $isSelectable={isSelectable} onClick={() => isSelectable && onSelectDate(date)} + data-cy={CYPRESS_DATA_MAP.calendar.dayCell} > - {date.getDate()} + {isCurrentMonth ? date.getDate() : ""} ); })} diff --git a/frontend/src/components/common/Chip/Chip.tsx b/frontend/src/components/common/Chip/Chip.tsx index c75e3b01..a238f39d 100644 --- a/frontend/src/components/common/Chip/Chip.tsx +++ b/frontend/src/components/common/Chip/Chip.tsx @@ -1,5 +1,7 @@ import { ComponentPropsWithoutRef } from "react"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import Text from "../Text/Text"; import * as S from "./Chip.styled"; @@ -10,7 +12,11 @@ interface ChipProps extends ComponentPropsWithoutRef<"li"> { const Chip = ({ isSelected = false, label, ...props }: ChipProps) => { return ( - + {isSelected ? ( {label} ) : ( diff --git a/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.constants.ts b/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.constants.ts new file mode 100644 index 00000000..9bdc7615 --- /dev/null +++ b/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.constants.ts @@ -0,0 +1,3 @@ +import { Libraries } from "@react-google-maps/api"; + +export const LIBRARIES: Libraries = ["maps", "places", "marker"]; diff --git a/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.tsx b/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.tsx index f52d3bb0..4ac78dbf 100644 --- a/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.tsx +++ b/frontend/src/components/common/GoogleMapLoadScript/GoogleMapLoadScript.tsx @@ -1,24 +1,18 @@ -import { Libraries, LoadScript } from "@react-google-maps/api"; +import { useJsApiLoader } from "@react-google-maps/api"; -interface GoogleMapLoadScriptProps { - libraries: Libraries; - loadingElement?: React.ReactNode; +import { LIBRARIES } from "@components/common/GoogleMapLoadScript/GoogleMapLoadScript.constants"; + +interface GoogleMapLoadScriptProps extends React.PropsWithChildren { + loadingElement: React.ReactNode; } -const GoogleMapLoadScript = ({ - children, - libraries, - loadingElement, -}: React.PropsWithChildren) => { - return ( - - {children} - - ); +const GoogleMapLoadScript = ({ children, loadingElement }: GoogleMapLoadScriptProps) => { + const { isLoaded } = useJsApiLoader({ + googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAP_API_KEY ?? "", + libraries: LIBRARIES, + }); + + return isLoaded ? <>{children} : loadingElement; }; export default GoogleMapLoadScript; diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts b/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts new file mode 100644 index 00000000..48b7c322 --- /dev/null +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts @@ -0,0 +1,22 @@ +export const GOOGLE_MAP_CONTAINER_STYLE = { + width: "100%", + height: "23rem", +}; + +export const INIT_CENTER_POSITION = { + lat: 37.5665, + lng: 126.978, +}; + +export const GOOGLE_MAP_OPTIONS = { + disableDefaultUI: true, + styles: [ + { + featureType: "poi", + elementType: "labels", + stylers: [{ visibility: "off" }], + }, + ], +}; + +export const POLYLINE_OPTIONS = { strokeColor: "#72A2FFCC", strokeWeight: 3 }; diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx b/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx index ad018725..79c05c1c 100644 --- a/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx @@ -1,16 +1,20 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useCallback } from "react"; +import { memo } from "react"; import { GoogleMap, MarkerF, Polyline } from "@react-google-maps/api"; -import theme from "@styles/theme"; +import { + GOOGLE_MAP_CONTAINER_STYLE, + GOOGLE_MAP_OPTIONS, + POLYLINE_OPTIONS, +} from "@components/common/GoogleMapView/GoogleMapView.constant"; +import { + calculateCenter, + createMarkerLabelStyle, +} from "@components/common/GoogleMapView/GoogleMapView.util"; -import { markerUrl } from "@assets/svg"; +import useGoogleMap from "@hooks/useGoogleMap"; -const containerStyle = { - width: "100%", - height: "23rem", -}; +import { markerUrl } from "@assets/svg"; interface GoogleMapViewProps { places: { @@ -19,72 +23,43 @@ interface GoogleMapViewProps { }[]; } -const calculateCenter = (places: { lat: number; lng: number }[]): { lat: number; lng: number } => { - const latSum = places.reduce((sum, place) => sum + place.lat, 0); - const lngSum = places.reduce((sum, place) => sum + place.lng, 0); - const count = places.length; - - return { - lat: latSum / count, - lng: lngSum / count, - }; -}; - const GoogleMapView = ({ places }: GoogleMapViewProps) => { const center = calculateCenter(places); - const onLoad = useCallback( - (map: google.maps.Map) => { - const bounds = new window.google.maps.LatLngBounds(); - places.forEach((place) => { - bounds.extend(new window.google.maps.LatLng(place.lat, place.lng)); - }); - map.fitBounds(bounds); - }, - [places], - ); + const { onLoad, onUnmount, onBoundsChanged } = useGoogleMap(places); + + /** + * 컴포넌트 내부에 위치시키지 않으면 "Uncaught TypeError: Cannot read properties of undefined (reading 'maps')" + * 에러가 발생하여 다음과 같은 위치로 변경 + */ + const MARKER_ICON_STYLE = { + url: markerUrl, + scaledSize: new window.google.maps.Size(30, 30), + labelOrigin: new window.google.maps.Point(15, -10), + }; return (
- {places.map((position, index) => ( - - ))} {places.map((position, index) => ( ))} - +
); }; -export default React.memo(GoogleMapView); +export default memo(GoogleMapView); diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts b/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts new file mode 100644 index 00000000..e8dc7ced --- /dev/null +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts @@ -0,0 +1,27 @@ +import { INIT_CENTER_POSITION } from "@components/common/GoogleMapView/GoogleMapView.constant"; + +import theme from "@styles/theme"; + +export const calculateCenter = ( + places: { lat: number; lng: number }[], +): { lat: number; lng: number } => { + if (places.length === 0) { + return { lat: INIT_CENTER_POSITION.lat, lng: INIT_CENTER_POSITION.lng }; + } + const latSum = places.reduce((sum, place) => sum + place.lat, 0); + const lngSum = places.reduce((sum, place) => sum + place.lng, 0); + const count = places.length; + + return { + lat: latSum / count, + lng: lngSum / count, + }; +}; + +export const createMarkerLabelStyle = (markerOrder: number) => { + return { + text: `${markerOrder + 1}`, + color: theme.colors.primary, + fontSize: "1.4rem", + }; +}; diff --git a/frontend/src/components/common/GoogleSearchPopup/GoogleSearchPopup.tsx b/frontend/src/components/common/GoogleSearchPopup/GoogleSearchPopup.tsx index 2c6c43d6..de5311bd 100644 --- a/frontend/src/components/common/GoogleSearchPopup/GoogleSearchPopup.tsx +++ b/frontend/src/components/common/GoogleSearchPopup/GoogleSearchPopup.tsx @@ -8,6 +8,8 @@ import type { TravelTransformPlace } from "@type/domain/travelTransform"; import { Button } from "@components/common"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import * as S from "./GoogleSearchPopup.styled"; interface GoogleSearchPopupProps { @@ -61,11 +63,16 @@ const GoogleSearchPopup = ({ onClosePopup, onSearchPlaceInfo }: GoogleSearchPopu }, [onPlaceChanged]); return ( - + - + diff --git a/frontend/src/components/common/Input/Input.stories.tsx b/frontend/src/components/common/Input/Input.stories.tsx index 09c3fdec..9aa851f5 100644 --- a/frontend/src/components/common/Input/Input.stories.tsx +++ b/frontend/src/components/common/Input/Input.stories.tsx @@ -21,8 +21,6 @@ type Story = StoryObj; export const Default: Story = { args: { placeholder: "제목을 입력해 주세요.", - count: 0, - maxCount: 20, }, }; diff --git a/frontend/src/components/common/Input/Input.styled.ts b/frontend/src/components/common/Input/Input.styled.ts index bf5e213a..289bba5f 100644 --- a/frontend/src/components/common/Input/Input.styled.ts +++ b/frontend/src/components/common/Input/Input.styled.ts @@ -5,14 +5,6 @@ import theme from "@styles/theme"; import type { InputVariants } from "./Input.type"; -export const InputContainer = styled.div` - display: flex; - flex-direction: column; - gap: 0.8rem; - - width: 100%; -`; - export const Label = styled.label` ${({ theme }) => theme.typography.mobile.bodyBold}; color: ${(props) => props.theme.colors.text.primary}; @@ -25,6 +17,7 @@ export const Input = styled.input<{ variant: InputVariants }>` ${({ theme }) => theme.typography.mobile.detail} color: ${({ theme }) => theme.colors.text.primary}; + font-size: 1.6rem; &:disabled { background-color: ${({ theme }) => theme.colors.background.disabled}; @@ -42,6 +35,7 @@ export const Input = styled.input<{ variant: InputVariants }>` if (variant === "none") return noneStyle; }} `; + export const roundStyle = css` border: 0.1rem solid ${theme.colors.border}; diff --git a/frontend/src/components/common/Input/Input.tsx b/frontend/src/components/common/Input/Input.tsx index 332b36c7..c25d236d 100644 --- a/frontend/src/components/common/Input/Input.tsx +++ b/frontend/src/components/common/Input/Input.tsx @@ -1,25 +1,15 @@ import { forwardRef } from "react"; -import CharacterCount from "../CharacterCount/CharacterCount"; import * as S from "./Input.styled"; import type { InputVariants } from "./Input.type"; interface InputProps extends React.ComponentPropsWithRef<"input"> { - count?: number; - maxCount?: number; variants?: InputVariants; } -const Input = forwardRef( - ({ count, maxCount, variants = "round", ...props }, ref) => { - return ( - - - {count && maxCount ? : null} - - ); - }, -); +const Input = forwardRef(({ variants = "round", ...props }, ref) => { + return ; +}); Input.displayName = "Input"; diff --git a/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx b/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx index 4c09357d..376318aa 100644 --- a/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx +++ b/frontend/src/components/common/ModalBottomSheet/ModalBottomSheet.tsx @@ -1,5 +1,7 @@ import useBottomSheet from "@hooks/useBottomSheet"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import Button from "../Button/Button"; import Spinner from "../Spinner/Spinner"; import BackDrop from "./BackDrop/BackDrop"; @@ -32,16 +34,25 @@ const ModalBottomSheet = ({ const { sheetRef, currentY } = useBottomSheet(isOpen, onClose); return isOpen ? ( -
+
- -
diff --git a/frontend/src/components/common/Spinner/Spinner.tsx b/frontend/src/components/common/Spinner/Spinner.tsx index b20139d7..462622d6 100644 --- a/frontend/src/components/common/Spinner/Spinner.tsx +++ b/frontend/src/components/common/Spinner/Spinner.tsx @@ -1,17 +1,19 @@ +import React from "react"; + import type { SpinnerVariants } from "@components/common/Spinner/Spinner.type"; import { Tturi } from "@assets/svg"; import * as S from "./Spinner.styled"; -interface SpinnerProps { +interface SpinnerProps extends React.ComponentPropsWithoutRef<"div"> { variants?: SpinnerVariants; size?: number; } -const Spinner = ({ variants = "tturi", size = 100 }: SpinnerProps) => { +const Spinner = ({ variants = "tturi", size = 100, ...props }: SpinnerProps) => { return ( - + {variants === "tturi" && } ); diff --git a/frontend/src/components/common/index.ts b/frontend/src/components/common/index.ts index 7e5cfb2b..e28828e9 100644 --- a/frontend/src/components/common/index.ts +++ b/frontend/src/components/common/index.ts @@ -29,3 +29,4 @@ export { default as TextField } from "./TextField/TextField"; export { default as Calendar } from "./Calendar/Calendar"; export { default as SearchFallback } from "./SearchFallback/SearchFallback"; export { default as FloatingButton } from "./FloatingButton/FloatingButton"; +export { default as Spinner } from "./Spinner/Spinner"; diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index e7cff24b..2f003c44 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -6,6 +6,7 @@ import { AvatarCircle, Chip, FallbackImage, IconButton, Text } from "@components import useImageError from "@hooks/useImageError"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; import { ROUTE_PATHS_MAP } from "@constants/route"; import * as S from "./TravelogueCard.styled"; @@ -41,7 +42,10 @@ const TravelogueCard = ({ }; return ( - + {title} diff --git a/frontend/src/components/pages/my/MyPage.styled.ts b/frontend/src/components/pages/my/MyPage.styled.ts index fb11c49c..dcaec82c 100644 --- a/frontend/src/components/pages/my/MyPage.styled.ts +++ b/frontend/src/components/pages/my/MyPage.styled.ts @@ -69,6 +69,14 @@ export const NicknameWrapper = styled.div` height: 3rem; `; +export const InputContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.s}; + + width: 100%; +`; + export const inputStyle = css` ${theme.typography.mobile.bodyBold}; width: 12rem; diff --git a/frontend/src/components/pages/my/MyPage.tsx b/frontend/src/components/pages/my/MyPage.tsx index 63fa1547..3c513831 100644 --- a/frontend/src/components/pages/my/MyPage.tsx +++ b/frontend/src/components/pages/my/MyPage.tsx @@ -3,7 +3,7 @@ import { FormEvent, MouseEvent, useEffect, useState } from "react"; import usePatchNickname from "@queries/usePatchNickname"; import { useUserProfile } from "@queries/useUserProfile"; -import { AvatarCircle, Input, Tab, Text } from "@components/common"; +import { AvatarCircle, CharacterCount, Input, Tab, Text } from "@components/common"; import MyPageSkeleton from "@components/pages/my/MyPageSkeleton/MyPageSkeleton"; import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; @@ -85,24 +85,28 @@ const MyPage = () => { {nickname} ) : ( - - setNickname( - e.target.value.slice( - FORM_VALIDATIONS_MAP.title.minLength, - FORM_VALIDATIONS_MAP.title.maxLength, - ), - ) - } - /> + + + setNickname( + e.target.value.slice( + FORM_VALIDATIONS_MAP.title.minLength, + FORM_VALIDATIONS_MAP.title.maxLength, + ), + ) + } + /> + + )} diff --git a/frontend/src/components/pages/travelPlanDetail/TravelPlansTabContent/TravelPlansTabContent.tsx b/frontend/src/components/pages/travelPlanDetail/TravelPlansTabContent/TravelPlansTabContent.tsx index b78e6ae9..43a900a8 100644 --- a/frontend/src/components/pages/travelPlanDetail/TravelPlansTabContent/TravelPlansTabContent.tsx +++ b/frontend/src/components/pages/travelPlanDetail/TravelPlansTabContent/TravelPlansTabContent.tsx @@ -17,10 +17,7 @@ const TravelPlansTabContent = ({ places }: { places: TravelPlanPlace[] }) => { return (
- } - libraries={["places", "maps"]} - > + }> diff --git a/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.styled.ts b/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.styled.ts index 556b8561..5a5d484e 100644 --- a/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.styled.ts +++ b/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.styled.ts @@ -7,34 +7,29 @@ import { SPACING } from "@styles/tokens"; export const Layout = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.xl}; + gap: ${({ theme }) => theme.spacing.xl}; - padding: ${SPACING.m}; - - & > :last-child { - margin-top: -${SPACING.xl}; - } + padding: ${({ theme }) => theme.spacing.m}; `; -export const AccordionRootContainer = styled.div` - margin-top: ${SPACING.m}; +export const InputContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.s}; + + width: 100%; `; export const PageInfoContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const StartDateContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; -`; - -export const LoadingWrapper = styled.div` - width: 100%; - height: 5.6rem; + gap: ${({ theme }) => theme.spacing.s}; `; export const addDayButtonStyle = css` @@ -47,26 +42,7 @@ export const addButtonStyle = css` width: 100%; height: 4rem; - margin-bottom: ${SPACING.xl}; padding: 1.2rem 1.6rem; - border: 0.1rem solid ${theme.colors.border}; + border: 1px solid ${theme.colors.border}; border-radius: ${SPACING.s}; `; - -export const loadingButtonStyle = css` - margin-top: ${SPACING.xl}; -`; - -export const startDateInputStyle = css` - margin: 0 0 ${SPACING.xl}; -`; - -export const calendarStyle = css` - margin-bottom: ${SPACING.xl}; -`; - -export const accordionRootStyle = css` - & > :last-child { - margin-bottom: ${SPACING.xl}; - } -`; diff --git a/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.tsx b/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.tsx index d59d80b4..2b9f718f 100644 --- a/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.tsx +++ b/frontend/src/components/pages/travelPlanEdit/TravelPlanEditPage.tsx @@ -7,6 +7,7 @@ import { usePutTravelPlan } from "@queries/usePutTravelPlan"; import { Accordion, Button, + CharacterCount, GoogleMapLoadScript, IconButton, Input, @@ -140,15 +141,19 @@ const TravelPlanEditPage = () => { {(id) => ( - + + + + )} @@ -165,36 +170,32 @@ const TravelPlanEditPage = () => { onClick={handleInputClick} readOnly placeholder="시작일을 입력해주세요" - css={S.startDateInputStyle} /> {isShowCalendar && ( setIsShowCalendar((prev) => !prev)} - css={S.calendarStyle} /> )} )} - + +
- onAddDay()} - > - 일자 추가하기 - - + onAddDay()} + > + 일자 추가하기 + } - libraries={["places", "maps"]} > - + {travelPlanDays.map((travelDay, dayIndex) => ( { onAddPlaceTodo={onAddPlaceTodo} /> ))} + + 일자 추가하기 + - onAddDay()} - > - 일자 추가하기 - - - +
+ - + ); }; diff --git a/frontend/src/components/pages/travelPlanRegister/TravelPlanDayAccordion/TravelPlanDayAccordion.tsx b/frontend/src/components/pages/travelPlanRegister/TravelPlanDayAccordion/TravelPlanDayAccordion.tsx index 2a69db92..00387aba 100644 --- a/frontend/src/components/pages/travelPlanRegister/TravelPlanDayAccordion/TravelPlanDayAccordion.tsx +++ b/frontend/src/components/pages/travelPlanRegister/TravelPlanDayAccordion/TravelPlanDayAccordion.tsx @@ -5,6 +5,8 @@ import { TravelPlanDay, TravelPlanPlace } from "@type/domain/travelPlan"; import { Accordion, GoogleMapView, GoogleSearchPopup, IconButton, Text } from "@components/common"; import PlaceTodoListItem from "@components/pages/travelPlanRegister/PlaceTodoListItem/PlaceTodoListItem"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; + import * as S from "../TravelPlanRegisterPage.styled"; interface TravelPlanDayAccordionProps { @@ -112,6 +114,7 @@ const TravelPlanDayAccordion = ({ position="left" css={[S.addTravelAddButtonStyle, S.addDayButtonStyle]} onClick={() => onAddPlaceTodo(dayIndex, placeIndex)} + data-cy={CYPRESS_DATA_MAP.travelPlanRegister.addTodoButton} > 할 일 추가하기 @@ -125,6 +128,7 @@ const TravelPlanDayAccordion = ({ position="left" css={[S.addTravelAddButtonStyle, S.addDayButtonStyle]} onClick={onClickAddPlaceButton} + data-cy={CYPRESS_DATA_MAP.travelPlanRegister.addPlaceButton} > 장소 추가하기 diff --git a/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.styled.ts b/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.styled.ts index 0a0dbc98..ffeb5722 100644 --- a/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.styled.ts +++ b/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.styled.ts @@ -7,29 +7,29 @@ import { SPACING } from "@styles/tokens"; export const Layout = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.xl}; + gap: ${({ theme }) => theme.spacing.xl}; - padding: ${SPACING.m}; - - & > :last-child { - margin-top: -${SPACING.xl}; - } + padding: ${({ theme }) => theme.spacing.m}; `; -export const AccordionRootContainer = styled.div` - margin-top: ${SPACING.m}; +export const InputContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.s}; + + width: 100%; `; export const PageInfoContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const StartDateContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const DayDetailText = styled.span` @@ -41,15 +41,10 @@ export const DayDetailText = styled.span` color: ${({ theme }) => theme.colors.text.secondary}; `; -export const LoadingWrapper = styled.div` - width: 100%; - height: 5.6rem; -`; - export const PlaceTodoListItemContainer = styled.ul` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const addDayButtonStyle = css` @@ -62,7 +57,6 @@ export const addButtonStyle = css` width: 100%; height: 4rem; - margin-bottom: 3.2rem; padding: 1.2rem 1.6rem; border: 1px solid ${theme.colors.border}; border-radius: ${SPACING.s}; @@ -78,21 +72,3 @@ export const addTravelAddButtonStyle = css` border: 1px solid ${theme.colors.border}; border-radius: ${SPACING.s}; `; - -export const loadingButtonStyle = css` - margin-top: ${SPACING.xl}; -`; - -export const startDateInputStyle = css` - margin-bottom: 3.2rem; -`; - -export const calendarStyle = css` - margin-bottom: ${SPACING.xl}; -`; - -export const accordionRootStyle = css` - & > :last-child { - margin-bottom: ${SPACING.xl}; - } -`; diff --git a/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.tsx b/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.tsx index 414f727c..7ad77d1d 100644 --- a/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.tsx +++ b/frontend/src/components/pages/travelPlanRegister/TravelPlanRegisterPage.tsx @@ -9,6 +9,7 @@ import { Accordion, Button, Calendar, + CharacterCount, GoogleMapLoadScript, IconButton, Input, @@ -19,10 +20,11 @@ import { } from "@components/common"; import TravelPlanDayAccordion from "@components/pages/travelPlanRegister/TravelPlanDayAccordion/TravelPlanDayAccordion"; -import { useTravelPlanDays } from "@hooks/pages/useTravelPlanDays"; +import useTravelPlanForm from "@hooks/pages/useTravelPlanForm"; import useLeadingDebounce from "@hooks/useLeadingDebounce"; import useUser from "@hooks/useUser"; +import { CYPRESS_DATA_MAP } from "@constants/cypress"; import { DEBOUNCED_TIME } from "@constants/debouncedTime"; import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; @@ -33,69 +35,63 @@ import { extractUTCDate } from "@utils/extractUTCDate"; import * as S from "./TravelPlanRegisterPage.styled"; const TravelPlanRegisterPage = () => { + /** form */ const { transformDetail, saveTransformDetail } = useTravelTransformDetailContext(); - const [title, setTitle] = useState(""); - - const [startDate, setStartDate] = useState(null); - const { - travelPlanDays, - onAddDay, - onAddPlace, - onDeleteDay, - onDeletePlace, - onAddPlaceTodo, - onDeletePlaceTodo, - onChangeContent, - } = useTravelPlanDays(transformDetail?.days ?? []); - - const handleChangeTitle = (e: React.ChangeEvent) => { - const title = e.target.value.slice( - FORM_VALIDATIONS_MAP.title.minLength, - FORM_VALIDATIONS_MAP.title.maxLength, - ); - setTitle(title); - }; + state: { title, startDate, travelPlanDays }, + handler: { + onChangeTitle, + onSelectCalendar, + onAddDay, + onAddPlace, + onDeleteDay, + onDeletePlace, + onAddPlaceTodo, + onDeletePlaceTodo, + onChangeContent, + }, + } = useTravelPlanForm(transformDetail?.days ?? []); + + /** ui */ + const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(false); + + const handleOpenBottomSheet = () => setIsOpenBottomSheet(true); + + const handleCloseBottomSheet = () => setIsOpenBottomSheet(false); - const [isOpen, setIsOpen] = useState(false); + const [isShowCalendar, setIsShowCalendar] = useState(false); - const handleOpenBottomSheet = () => { - setIsOpen(true); - }; + const handleOpenCalendar = () => setIsShowCalendar(true); - const handleCloseBottomSheet = () => { - setIsOpen(false); - }; + const handleCloseCalendar = () => setIsShowCalendar(() => false); + /** router */ const navigate = useNavigate(); + /** server */ const { mutate: mutateTravelPlanRegister, isPending: isPostingTravelPlanPending } = usePostTravelPlan(); const handleRegisterTravelPlan = () => { - const formattedStartDate = extractUTCDate(startDate); - - mutateTravelPlanRegister( - { title, startDate: formattedStartDate, days: travelPlanDays }, - { - onSuccess: (data) => { - handleCloseBottomSheet(); - navigate(ROUTE_PATHS_MAP.travelPlan(data?.data?.id)); - }, + const travelPlanPayload = { title, startDate: extractUTCDate(startDate), days: travelPlanDays }; + + mutateTravelPlanRegister(travelPlanPayload, { + onSuccess: (data) => { + const travelPlanId = data?.data?.id; + + handleCloseBottomSheet(); + navigate(ROUTE_PATHS_MAP.travelPlan(travelPlanId)); }, - ); + }); }; - const debouncedRegisterTravelPlan = useLeadingDebounce( + const handleConfirmBottomSheet = useLeadingDebounce( () => handleRegisterTravelPlan(), DEBOUNCED_TIME, ); - const handleConfirmBottomSheet = () => { - debouncedRegisterTravelPlan(); - }; - + /** authorization */ const { user } = useUser(); useEffect(() => { @@ -108,17 +104,6 @@ const TravelPlanRegisterPage = () => { }; }, [user?.accessToken, navigate, saveTransformDetail]); - const [isShowCalendar, setIsShowCalendar] = useState(false); - - const handleInputClick = () => { - setIsShowCalendar(true); - }; - - const handleSelectDate = (date: Date) => { - setStartDate(date); - setIsShowCalendar(false); - }; - return ( <> @@ -128,15 +113,20 @@ const TravelPlanRegisterPage = () => { /> {(id) => ( - + + onChangeTitle(event.target.value)} + data-cy={CYPRESS_DATA_MAP.travelPlanRegister.titleInput} + /> + + )} @@ -150,39 +140,41 @@ const TravelPlanRegisterPage = () => { {isShowCalendar && ( setIsShowCalendar((prev) => !prev)} - css={S.calendarStyle} + onSelectDate={(date) => onSelectCalendar(date, handleCloseCalendar)} + onClose={handleCloseCalendar} /> )} )} - + +
- onAddDay()} + onAddDay()} + > + - 일자 추가하기 - - + 일자 추가하기 + + } - libraries={["places", "maps"]} > - + {travelPlanDays.map((travelDay, dayIndex) => ( { onAddPlaceTodo={onAddPlaceTodo} /> ))} + + + 일자 추가하기 + + - onAddDay()} - > - 일자 추가하기 - - - +
+ +
{ return ( - } - libraries={["maps"]} - > + }> ({ lat: Number(place.position.lat), diff --git a/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.styled.ts b/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.styled.ts index 694fac0e..8a87dd11 100644 --- a/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.styled.ts +++ b/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.styled.ts @@ -7,24 +7,32 @@ import { PRIMITIVE_COLORS, SPACING } from "@styles/tokens"; export const Layout = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.xl}; + gap: ${({ theme }) => theme.spacing.xl}; - padding: ${SPACING.m}; + padding: ${({ theme }) => theme.spacing.m}; +`; + +export const InputContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.s}; + + width: 100%; `; export const TagsContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const ChipsContainer = styled.ul` display: flex; overflow: scroll hidden; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; - margin: 0 -${SPACING.m}; - padding: 0 ${SPACING.m}; + margin: 0 -${({ theme }) => theme.spacing.m}; + padding: 0 ${({ theme }) => theme.spacing.m}; white-space: nowrap; -webkit-overflow-scrolling: touch; @@ -43,12 +51,7 @@ export const ChipsContainer = styled.ul` export const ThumbnailContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; -`; - -export const LoadingWrapper = styled.div` - width: 100%; - height: 5.6rem; + gap: ${({ theme }) => theme.spacing.s}; `; export const subTextColor = css` @@ -61,14 +64,9 @@ export const addButtonStyle = css` width: 100%; height: 4rem; - margin-bottom: 3.2rem; padding: 1.2rem 1.6rem; border: 1px solid ${theme.colors.border}; border-radius: ${SPACING.s}; - - color: ${PRIMITIVE_COLORS.black}; - font-weight: 700; - font-size: 1.6rem; `; export const addTravelAddButtonStyle = css` @@ -88,13 +86,3 @@ export const addTravelAddButtonStyle = css` export const addDayButtonStyle = css` margin-top: 1.6rem; `; - -export const loadingButtonStyle = css` - margin-top: 3.2rem; -`; - -export const accordionRootStyle = css` - & > :last-child { - margin-bottom: 3.2rem; - } -`; diff --git a/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.tsx b/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.tsx index 40c83796..a547305a 100644 --- a/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.tsx +++ b/frontend/src/components/pages/travelogueEdit/TravelogueEditPage.tsx @@ -8,12 +8,14 @@ import { usePutTravelogue } from "@queries/usePutTravelogue"; import { Accordion, Button, + CharacterCount, Chip, GoogleMapLoadScript, IconButton, Input, ModalBottomSheet, PageInfo, + Text, TextField, ThumbnailUpload, } from "@components/common"; @@ -151,15 +153,19 @@ const TravelogueEditPage = () => { {(id) => ( - + + + + )} @@ -207,21 +213,18 @@ const TravelogueEditPage = () => {
- onAddDay()} - > - 일자 추가하기 - - + onAddDay()} + > + 일자 추가하기 + } - libraries={["places", "maps"]} > - + {travelogueDays.map((travelogueDay, dayIndex) => ( { onRequestAddImage={mutateAddImage} /> ))} + onAddDay()} + > + 일자 추가하기 + - onAddDay()} - > - 일자 추가하기 - -
+ + resizeAndConvertImage(file))); + + const newImageUrls = await onRequestAddImage(processedFiles); handleUploadSuccess(newImageUrls); } catch (error) { if (error instanceof Error) alert(error.message); - revertImageStates(files.length); } finally { resetFileInput(); diff --git a/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.styled.ts b/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.styled.ts index 694fac0e..8a87dd11 100644 --- a/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.styled.ts +++ b/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.styled.ts @@ -7,24 +7,32 @@ import { PRIMITIVE_COLORS, SPACING } from "@styles/tokens"; export const Layout = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.xl}; + gap: ${({ theme }) => theme.spacing.xl}; - padding: ${SPACING.m}; + padding: ${({ theme }) => theme.spacing.m}; +`; + +export const InputContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.s}; + + width: 100%; `; export const TagsContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; `; export const ChipsContainer = styled.ul` display: flex; overflow: scroll hidden; - gap: ${SPACING.s}; + gap: ${({ theme }) => theme.spacing.s}; - margin: 0 -${SPACING.m}; - padding: 0 ${SPACING.m}; + margin: 0 -${({ theme }) => theme.spacing.m}; + padding: 0 ${({ theme }) => theme.spacing.m}; white-space: nowrap; -webkit-overflow-scrolling: touch; @@ -43,12 +51,7 @@ export const ChipsContainer = styled.ul` export const ThumbnailContainer = styled.div` display: flex; flex-direction: column; - gap: ${SPACING.s}; -`; - -export const LoadingWrapper = styled.div` - width: 100%; - height: 5.6rem; + gap: ${({ theme }) => theme.spacing.s}; `; export const subTextColor = css` @@ -61,14 +64,9 @@ export const addButtonStyle = css` width: 100%; height: 4rem; - margin-bottom: 3.2rem; padding: 1.2rem 1.6rem; border: 1px solid ${theme.colors.border}; border-radius: ${SPACING.s}; - - color: ${PRIMITIVE_COLORS.black}; - font-weight: 700; - font-size: 1.6rem; `; export const addTravelAddButtonStyle = css` @@ -88,13 +86,3 @@ export const addTravelAddButtonStyle = css` export const addDayButtonStyle = css` margin-top: 1.6rem; `; - -export const loadingButtonStyle = css` - margin-top: 3.2rem; -`; - -export const accordionRootStyle = css` - & > :last-child { - margin-bottom: 3.2rem; - } -`; diff --git a/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.tsx b/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.tsx index 1b9dc924..ce6b8b82 100644 --- a/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.tsx +++ b/frontend/src/components/pages/travelogueRegister/TravelogueRegisterPage.tsx @@ -8,6 +8,7 @@ import { usePostTravelogue, usePostUploadImages } from "@queries/index"; import { Accordion, Button, + CharacterCount, Chip, GoogleMapLoadScript, IconButton, @@ -31,6 +32,8 @@ import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; import { ROUTE_PATHS_MAP } from "@constants/route"; +import resizeAndConvertImage from "@utils/resizeAndConvertImage"; + import * as S from "./TravelogueRegisterPage.styled"; const TravelogueRegisterPage = () => { @@ -76,7 +79,11 @@ const TravelogueRegisterPage = () => { const handleChangeThumbnail = async (e: React.ChangeEvent) => { try { - const thumbnail = await mutateAddImage(Array.from(e.target.files as FileList)); + const files = Array.from(e.target.files as FileList); + + const processedFiles = await Promise.all(files.map((file) => resizeAndConvertImage(file))); + + const thumbnail = await mutateAddImage(processedFiles); setThumbnail(thumbnail[0]); } catch (error) { if (error instanceof Error) { @@ -146,14 +153,18 @@ const TravelogueRegisterPage = () => { {(id) => ( - + + + + )} @@ -197,21 +208,18 @@ const TravelogueRegisterPage = () => {
- onAddDay()} - > - 일자 추가하기 - - + onAddDay()} + > + 일자 추가하기 + } - libraries={["places", "maps"]} > - + {travelogueDays.map((travelogueDay, dayIndex) => ( { onRequestAddImage={mutateAddImage} /> ))} + onAddDay()} + > + 일자 추가하기 + - onAddDay()} - > - 일자 추가하기 - -
+ + { - const transformedDetailData = [...days].map((day) => { - return { - ...day, - places: day.places.map((place) => { - return { - ...place, - todos: [], - }; - }), - }; - }); - - const [travelPlanDays, setTravelPlanDays] = useState(transformedDetailData); - - const onChangeTravelPlanDays = useCallback((newDays: TravelPlanDay[]) => { - setTravelPlanDays(newDays); - }, []); - - const onAddDay = useCallback((dayIndex?: number) => { - setTravelPlanDays((prevTravelDays) => - dayIndex - ? Array.from({ length: dayIndex }, () => ({ id: uuidv4(), places: [] })) - : [...prevTravelDays, { id: uuidv4(), places: [] }], - ); - }, []); - - const onDeleteDay = (targetDayIndex: number) => { - setTravelPlanDays((prevTravelDays) => - prevTravelDays.filter((_, dayIndex) => dayIndex !== targetDayIndex), - ); - }; - - const onAddPlace = ( - dayIndex: number, - travelParams: Pick, - ) => { - setTravelPlanDays((prevTravelDays) => { - const newTravelPlans = [...prevTravelDays]; - newTravelPlans[dayIndex].places.push({ ...travelParams, id: uuidv4(), todos: [] }); - return newTravelPlans; - }); - }; - - const onDeletePlace = (dayIndex: number, placeIndex: number) => { - setTravelPlanDays((prevTravelDays) => { - const newTravelPlans = [...prevTravelDays]; - newTravelPlans[dayIndex] = { - ...newTravelPlans[dayIndex], - places: newTravelPlans[dayIndex].places.filter((_, index) => index !== placeIndex), +const transformTravelPlanDays = (days: TravelTransformPlaces[]) => { + return [...days].map((day) => ({ + ...day, + places: day.places.map((place) => { + return { + ...place, + todos: [], }; + }), + })); +}; - return newTravelPlans; - }); - }; - - const onChangeContent = ({ - content, - dayIndex, - placeIndex, - todoId, - }: { - content: string; - dayIndex: number; - placeIndex: number; - todoId: string; - }) => { - setTravelPlanDays((prevTravelPlansDays) => { - const newTravelPlans = [...prevTravelPlansDays]; - const place = newTravelPlans[dayIndex]?.places[placeIndex]; - if (!place?.todos) return prevTravelPlansDays; - - const todoIndex = place.todos.findIndex((todo) => todo.id === todoId); - if (todoIndex === -1) return prevTravelPlansDays; - - place.todos = place.todos.map((todo, index) => - index === todoIndex - ? { - ...todo, - content: content.slice( - FORM_VALIDATIONS_MAP.title.minLength, - FORM_VALIDATIONS_MAP.title.maxLength, - ), - } - : todo, - ); - - return newTravelPlans; - }); - }; - - const onAddPlaceTodo = (dayIndex: number, placeIndex: number) => { - setTravelPlanDays((prevTravelPlansDays) => { - const newTravelPlans = [...prevTravelPlansDays]; - const place = newTravelPlans[dayIndex]?.places[placeIndex]; - if (!place) return prevTravelPlansDays; - - place.todos = [...(place.todos ?? []), { id: uuidv4(), content: "", checked: false }]; - - return newTravelPlans; - }); - }; - - const onDeletePlaceTodo = (dayIndex: number, placeIndex: number, todoId: string) => { - setTravelPlanDays((prevTravelPlanDays) => { - const newTravelPlans = [...prevTravelPlanDays]; - const place = newTravelPlans[dayIndex]?.places[placeIndex]; - if (!place?.todos) return prevTravelPlanDays; - - place.todos = place.todos.filter((todo) => todo.id !== todoId); - - return newTravelPlans; +export const useTravelPlanDays = (days: TravelTransformPlaces[]) => { + const [travelPlanDays, setTravelPlanDays] = useImmer(() => + transformTravelPlanDays(days), + ); + + const onChangeTravelPlanDays = useCallback( + (newDays: TravelPlanDay[]) => { + setTravelPlanDays(newDays); + }, + [setTravelPlanDays], + ); + + const onAddDay = useCallback(() => { + setTravelPlanDays((previousTravelPlanDays) => { + previousTravelPlanDays.push({ id: uuidv4(), places: [] }); }); - }; + }, [setTravelPlanDays]); + + const onDeleteDay = useCallback( + (targetDayIndex: number) => { + setTravelPlanDays((previousTravelPlanDays) => { + previousTravelPlanDays.splice(targetDayIndex, 1); + }); + }, + [setTravelPlanDays], + ); + + const onAddPlace = useCallback( + (dayIndex: number, travelParams: Pick) => { + setTravelPlanDays((previousTravelPlanDays) => { + const travelPlanDay = previousTravelPlanDays[dayIndex]; + + if (travelPlanDay) { + travelPlanDay.places.push({ + ...travelParams, + id: uuidv4(), + todos: [], + }); + } + }); + }, + [setTravelPlanDays], + ); + + const onDeletePlace = useCallback( + (dayIndex: number, placeIndex: number) => { + setTravelPlanDays((previousTravelPlanDays) => { + const travelPlanPlaces = previousTravelPlanDays[dayIndex]?.places; + + if (travelPlanPlaces) { + travelPlanPlaces.splice(placeIndex, 1); + } + }); + }, + [setTravelPlanDays], + ); + + const onChangeContent = useCallback( + ({ + content, + dayIndex, + placeIndex, + todoId, + }: { + content: string; + dayIndex: number; + placeIndex: number; + todoId: string; + }) => { + setTravelPlanDays((previousTravelPlanDays) => { + const todo = previousTravelPlanDays[dayIndex]?.places[placeIndex]?.todos?.find( + (todo) => todo.id === todoId, + ); + + if (todo) { + todo.content = content.slice( + FORM_VALIDATIONS_MAP.title.minLength, + FORM_VALIDATIONS_MAP.title.maxLength, + ); + } + }); + }, + [setTravelPlanDays], + ); + + const onAddPlaceTodo = useCallback( + (dayIndex: number, placeIndex: number) => { + setTravelPlanDays((previousTravelPlanDays) => { + const travelPlanPlace = previousTravelPlanDays[dayIndex]?.places[placeIndex]; + + if (travelPlanPlace) { + travelPlanPlace.todos?.push({ id: uuidv4(), content: "", checked: false }); + } + }); + }, + [setTravelPlanDays], + ); + + const onDeletePlaceTodo = useCallback( + (dayIndex: number, placeIndex: number, todoId: string) => { + setTravelPlanDays((previousTravelPlanDays) => { + const travelPlanPlace = previousTravelPlanDays[dayIndex]?.places[placeIndex]; + + if (travelPlanPlace?.todos) { + travelPlanPlace.todos = travelPlanPlace.todos.filter((todo) => todo.id !== todoId); + } + }); + }, + [setTravelPlanDays], + ); return { travelPlanDays, diff --git a/frontend/src/hooks/pages/useTravelPlanForm.ts b/frontend/src/hooks/pages/useTravelPlanForm.ts new file mode 100644 index 00000000..f9577f88 --- /dev/null +++ b/frontend/src/hooks/pages/useTravelPlanForm.ts @@ -0,0 +1,59 @@ +import { useState } from "react"; + +import { TravelTransformPlaces } from "@type/domain/travelTransform"; + +import { useTravelPlanDays } from "@hooks/pages/useTravelPlanDays"; + +import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; + +const useTravelPlanForm = (transformDays: TravelTransformPlaces[]) => { + const [title, setTitle] = useState(""); + + const onChangeTitle = (inputValue: string) => { + const trimmedTitle = inputValue.slice( + FORM_VALIDATIONS_MAP.title.minLength, + FORM_VALIDATIONS_MAP.title.maxLength, + ); + + setTitle(trimmedTitle); + }; + + const [startDate, setStartDate] = useState(null); + + const onSelectCalendar = (date: Date, handleCloseCalendar: () => void) => { + setStartDate(date); + handleCloseCalendar(); + }; + + const { + travelPlanDays, + onAddDay, + onAddPlace, + onDeleteDay, + onDeletePlace, + onAddPlaceTodo, + onDeletePlaceTodo, + onChangeContent, + } = useTravelPlanDays(transformDays); + + return { + state: { + title, + startDate, + travelPlanDays, + }, + handler: { + onChangeTitle, + onSelectCalendar, + onAddDay, + onAddPlace, + onDeleteDay, + onDeletePlace, + onAddPlaceTodo, + onDeletePlaceTodo, + onChangeContent, + }, + }; +}; + +export default useTravelPlanForm; diff --git a/frontend/src/hooks/useGoogleMap.ts b/frontend/src/hooks/useGoogleMap.ts new file mode 100644 index 00000000..56d049d4 --- /dev/null +++ b/frontend/src/hooks/useGoogleMap.ts @@ -0,0 +1,43 @@ +import { useCallback, useState } from "react"; + +import { MapPosition } from "@type/domain/common"; + +const INIT_CENTER_POSITION = { + lat: 37.5665, + lng: 126.978, +}; + +const useGoogleMap = (places: MapPosition[]) => { + const [googleMap, setGoogleMap] = useState(null); + + const onLoad = useCallback((map: google.maps.Map) => { + setGoogleMap(map); + }, []); + + const onUnmount = useCallback(() => { + setGoogleMap(null); + }, []); + + const onBoundsChanged = useCallback(() => { + if (googleMap) { + if (places.length === 0) { + googleMap.setCenter(INIT_CENTER_POSITION); + googleMap.setZoom(7); + } else { + const bounds = new window.google.maps.LatLngBounds(); + places.forEach((place) => { + bounds.extend(new window.google.maps.LatLng(place.lat, place.lng)); + }); + googleMap.fitBounds(bounds); + } + } + }, [places, googleMap]); + + return { + onLoad, + onUnmount, + onBoundsChanged, + }; +}; + +export default useGoogleMap; diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index ff744c68..9030fb02 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,21 +1,58 @@ +import { Suspense, lazy } from "react"; import { createBrowserRouter } from "react-router-dom"; +import { css } from "@emotion/react"; + +import { Spinner, Text } from "@components/common"; import AppLayout from "@components/layout/AppLayout/AppLayout"; import KakaoCallbackPage from "@components/pages/login/KakaoCallbackPage"; import LoginPage from "@components/pages/login/LoginPage"; import MainPage from "@components/pages/main/MainPage"; import MyPage from "@components/pages/my/MyPage"; import NotFoundPage from "@components/pages/notFound/NotFoundPage"; -import SearchPage from "@components/pages/search/SearchPage"; import TravelPlanDetailPage from "@components/pages/travelPlanDetail/TravelPlanDetailPage"; -import TravelPlanEditPage from "@components/pages/travelPlanEdit/TravelPlanEditPage"; -import TravelPlanRegisterPage from "@components/pages/travelPlanRegister/TravelPlanRegisterPage"; import TravelogueDetailPage from "@components/pages/travelogueDetail/TravelogueDetailPage"; -import TravelogueEditPage from "@components/pages/travelogueEdit/TravelogueEditPage"; -import TravelogueRegisterPage from "@components/pages/travelogueRegister/TravelogueRegisterPage"; import { ROUTE_PATHS_MAP } from "./constants/route"; +const TravelogueRegisterPage = lazy( + () => import("@components/pages/travelogueRegister/TravelogueRegisterPage"), +); +const TravelogueEditPage = lazy( + () => import("@components/pages/travelogueEdit/TravelogueEditPage"), +); +const TravelPlanRegisterPage = lazy( + () => import("@components/pages/travelPlanRegister/TravelPlanRegisterPage"), +); +const TravelPlanEditPage = lazy( + () => import("@components/pages/travelPlanEdit/TravelPlanEditPage"), +); +const SearchPage = lazy(() => import("@components/pages/search/SearchPage")); + +const lazyLoadingFallbackStyle = css` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100vh; + max-width: 48rem; + gap: 1rem; +`; + +const withLazyLoading = (Component: React.ComponentType) => ( + + + 로딩 중이에요. 잠시만 기다려주세요 :) +
+ } + > + + +); + export const router = createBrowserRouter([ { path: ROUTE_PATHS_MAP.root, @@ -29,7 +66,6 @@ export const router = createBrowserRouter([ path: ROUTE_PATHS_MAP.login, element: , }, - { path: ROUTE_PATHS_MAP.loginCallback, element: , @@ -40,11 +76,11 @@ export const router = createBrowserRouter([ }, { path: ROUTE_PATHS_MAP.travelogueRegister, - element: , + element: withLazyLoading(TravelogueRegisterPage), }, { path: ROUTE_PATHS_MAP.travelogueEdit(), - element: , + element: withLazyLoading(TravelogueEditPage), }, { path: ROUTE_PATHS_MAP.travelPlan(), @@ -52,11 +88,11 @@ export const router = createBrowserRouter([ }, { path: ROUTE_PATHS_MAP.travelPlanRegister, - element: , + element: withLazyLoading(TravelPlanRegisterPage), }, { path: ROUTE_PATHS_MAP.travelPlanEdit(), - element: , + element: withLazyLoading(TravelPlanEditPage), }, { path: ROUTE_PATHS_MAP.my, @@ -68,7 +104,7 @@ export const router = createBrowserRouter([ }, { path: ROUTE_PATHS_MAP.search(), - element: , + element: withLazyLoading(SearchPage), }, { path: "*", diff --git a/frontend/src/styles/globalStyle.ts b/frontend/src/styles/globalStyle.ts index 5973d14b..f3c832b1 100644 --- a/frontend/src/styles/globalStyle.ts +++ b/frontend/src/styles/globalStyle.ts @@ -1,13 +1,22 @@ import { css } from "@emotion/react"; +import pretendard_black from "../assets/fonts/woff-subset/Pretendard-Black.subset.woff"; +import pretendard_bold from "../assets/fonts/woff-subset/Pretendard-Bold.subset.woff"; +import pretendard_extra_bold from "../assets/fonts/woff-subset/Pretendard-ExtraBold.subset.woff"; +import pretendard_extra_light from "../assets/fonts/woff-subset/Pretendard-ExtraLight.subset.woff"; +import pretendard_light from "../assets/fonts/woff-subset/Pretendard-Light.subset.woff"; +import pretendard_medium from "../assets/fonts/woff-subset/Pretendard-Medium.subset.woff"; +import pretendard_regular from "../assets/fonts/woff-subset/Pretendard-Regular.subset.woff"; +import pretendard_semi_bold from "../assets/fonts/woff-subset/Pretendard-SemiBold.subset.woff"; +import pretendard_thin from "../assets/fonts/woff-subset/Pretendard-Thin.subset.woff"; + export const globalStyle = css` @font-face { font-family: Pretendard; font-weight: 900; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-Black.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_black}) format("woff"); } @font-face { @@ -15,8 +24,7 @@ export const globalStyle = css` font-weight: 800; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-ExtraBold.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_extra_bold}) format("woff"); } @font-face { @@ -24,8 +32,7 @@ export const globalStyle = css` font-weight: 700; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-Bold.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_bold}) format("woff"); } @font-face { @@ -33,8 +40,7 @@ export const globalStyle = css` font-weight: 600; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-SemiBold.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_semi_bold}) format("woff"); } @font-face { @@ -42,8 +48,7 @@ export const globalStyle = css` font-weight: 500; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-Medium.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_medium}) format("woff"); } @font-face { @@ -51,8 +56,7 @@ export const globalStyle = css` font-weight: 400; font-display: swap; - src: local("Pretendard") url("/@assets/fonts/woff-subset/Pretendard-Regular.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_regular}) format("woff"); } @font-face { @@ -60,8 +64,7 @@ export const globalStyle = css` font-weight: 300; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-Light.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_light}) format("woff"); } @font-face { @@ -69,8 +72,7 @@ export const globalStyle = css` font-weight: 200; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-ExtraLight.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_extra_light}) format("woff"); } @font-face { @@ -78,8 +80,7 @@ export const globalStyle = css` font-weight: 100; font-display: swap; - src: local("Pretendard") url("@assets/fonts/woff-subset/Pretendard-Thin.subset.woff") - format("woff"); + src: local("Pretendard") url(${pretendard_thin}) format("woff"); } html, diff --git a/frontend/src/utils/cypress.ts b/frontend/src/utils/cypress.ts new file mode 100644 index 00000000..2970ee71 --- /dev/null +++ b/frontend/src/utils/cypress.ts @@ -0,0 +1,24 @@ +type SelectorMap = { + [K in keyof T]: T[K] extends string ? string : T[K] extends object ? SelectorMap : never; +}; + +export const generateSelectors = ( + dataMap: T, + prefix: string = "", +): SelectorMap => { + const result = {} as SelectorMap; + + for (const [key, value] of Object.entries(dataMap)) { + if (typeof value === "string") { + result[key as keyof T] = + `[data-cy="${prefix}${value}"]` as (typeof result)[keyof typeof result]; + } else if (typeof value === "object" && value !== null) { + result[key as keyof T] = generateSelectors( + value, + prefix, + ) as (typeof result)[keyof typeof result]; + } + } + + return result; +}; diff --git a/frontend/src/utils/resizeAndConvertImage.ts b/frontend/src/utils/resizeAndConvertImage.ts new file mode 100644 index 00000000..8e34c0f4 --- /dev/null +++ b/frontend/src/utils/resizeAndConvertImage.ts @@ -0,0 +1,89 @@ +import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; +import { MAX_HEIGHT, MAX_WIDTH } from "@constants/resize"; + +const loadImageFromFile = (file: File): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = (error) => reject(error); + img.src = URL.createObjectURL(file); + }); +}; + +const resizeImage = ( + img: HTMLImageElement, + maxWidth: number, + maxHeight: number, +): HTMLCanvasElement => { + const canvas = document.createElement("canvas"); + let width = img.width; + let height = img.height; + + if (width > height) { + if (width > maxWidth) { + height *= maxWidth / width; + width = maxWidth; + } + } else { + if (height > maxHeight) { + width *= maxHeight / height; + height = maxHeight; + } + } + + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext("2d"); + ctx!.drawImage(img, 0, 0, width, height); + return canvas; +}; + +const convertCanvasToBlob = ( + canvas: HTMLCanvasElement, + format: string = "image/webp", + quality: number = 0.8, +): Promise => { + return new Promise((resolve, reject) => { + canvas.toBlob( + (blob) => { + if (blob) { + resolve(blob); + } else { + reject(new Error(ERROR_MESSAGE_MAP.imageConvert)); + } + }, + format, + quality, + ); + }); +}; + +const createFileFromBlob = ( + blob: Blob, + originalFileName: string, + newExtension: string = "webp", +): File => { + const fileName = originalFileName.replace(/\.[^/.]+$/, "") + `.${newExtension}`; + return new File([blob], fileName, { + type: `image/${newExtension}`, + lastModified: new Date().getTime(), + }); +}; + +const resizeAndConvertImage = async ( + file: File, + maxWidth: number = MAX_WIDTH, + maxHeight: number = MAX_HEIGHT, +): Promise => { + try { + const img = await loadImageFromFile(file); // 1. 이미지 로드 + const resizedCanvas = resizeImage(img, maxWidth, maxHeight); // 2. 이미지 리사이즈 + const blob = await convertCanvasToBlob(resizedCanvas); // 3. Blob으로 변환 + const newFile = createFileFromBlob(blob, file.name); // 4. Blob을 파일로 변환 + return newFile; + } catch (error) { + throw new Error(ERROR_MESSAGE_MAP.imageConvert); + } +}; + +export default resizeAndConvertImage; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 402b44da..71f48ccf 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -27,9 +27,9 @@ "@contexts/*": ["contexts/*"], "@utils/*": ["utils/*"] }, - "types": ["jest", "node"], + "types": ["node", "jest"], "esModuleInterop": true }, - "include": ["src", "jest-setup.ts", "__tests__"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "cypress"], + "include": ["src", "jest-setup.ts", "__tests__"] } diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 904b799a..35dae068 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -45,8 +45,18 @@ module.exports = { use: ["@svgr/webpack"], }, { - test: /\.(png|jpg|jpeg|gif|woff|webp)$/i, + test: /\.(png|jpg|jpeg|gif|webp|avif)$/i, type: "asset/resource", + generator: { + filename: "assets/images/[name].[contenthash:8][ext]", + }, + }, + { + test: /\.(woff)$/i, + type: "asset/resource", + generator: { + filename: "assets/fonts/[name].[contenthash:8][ext]", + }, }, { test: /\.(ts|tsx)$/i, diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js index 68c16858..bb0581b0 100644 --- a/frontend/webpack.production.js +++ b/frontend/webpack.production.js @@ -3,11 +3,18 @@ const { sentryWebpackPlugin } = require("@sentry/webpack-plugin"); const common = require("./webpack.common"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const dotenv = require("dotenv"); +// const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const path = require("path"); const env = dotenv.config({ path: ".env.production" }).parsed; module.exports = merge(common, { mode: "production", + output: { + publicPath: "/", + filename: "[name].[contenthash:8].js", + path: path.resolve(__dirname, "dist"), + }, devtool: "hidden-source-map", cache: { type: "filesystem", @@ -25,5 +32,14 @@ module.exports = merge(common, { filesToDeleteAfterUpload: "**/*.js.map", }, }), + /** TODO: 필요할 때 주석 해제해서 번들 사이즈 확인하기! */ + // new BundleAnalyzerPlugin({ + // openAnalyzer: true, + // }), ], + optimization: { + splitChunks: { + chunks: "all", + }, + }, }); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9d1cc684..c7511b64 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1199,6 +1199,11 @@ react-confetti "^6.1.0" strip-ansi "^7.1.0" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@csstools/css-parser-algorithms@^2.6.3": version "2.7.1" resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz#6d93a8f7d8aeb7cd9ed0868f946e46f021b6aa70" @@ -1219,7 +1224,39 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz#63085d2995ca0f0e55aa8b8a07d69bfd48b844fe" integrity sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA== -"@discoveryjs/json-ext@^0.5.0": +"@cypress/request@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" + integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + http-signature "~1.3.6" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "6.10.4" + safe-buffer "^5.1.2" + tough-cookie "^4.1.3" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== @@ -1914,6 +1951,11 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.28" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== + "@react-google-maps/api@^2.19.3": version "2.19.3" resolved "https://registry.yarnpkg.com/@react-google-maps/api/-/api-2.19.3.tgz#6bb307c6e9d06344badd77facb6dc9eaac5b9b32" @@ -3116,6 +3158,16 @@ "@types/node" "*" "@types/send" "*" +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== + +"@types/sizzle@^2.3.2": + version "2.3.8" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627" + integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg== + "@types/sockjs@^0.3.36": version "0.3.36" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" @@ -3172,6 +3224,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^7.16.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" @@ -3575,6 +3634,13 @@ acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.0.0: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + acorn-walk@^8.0.2: version "8.3.3" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" @@ -3587,7 +3653,7 @@ acorn@^7.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.1.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -3599,6 +3665,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -3638,7 +3712,12 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -3692,6 +3771,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3798,6 +3882,18 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -3822,16 +3918,26 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async@^3.2.3: +async@^3.2.0: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== +async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -3839,6 +3945,16 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.1.tgz#bb5f8b8a20739f6ae1caeaf7eea2c7913df8048e" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== + axios@^1.7.2: version "1.7.4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" @@ -3977,6 +4093,13 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -3991,6 +4114,16 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + body-parser@1.20.2: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" @@ -4073,12 +4206,17 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0: +buffer@^5.5.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -4103,6 +4241,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cachedir@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" + integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -4147,6 +4290,11 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + chai@^4.3.10: version "4.5.0" resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" @@ -4197,6 +4345,11 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -4251,6 +4404,11 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -4263,6 +4421,23 @@ cli-spinners@^2.5.0, cli-spinners@^2.9.2: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== +cli-table3@~0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-width@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" @@ -4330,12 +4505,12 @@ colord@^2.9.3: resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -colorette@^2.0.10, colorette@^2.0.14: +colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -combined-stream@^1.0.8: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -4372,6 +4547,11 @@ common-path-prefix@^3.0.0: resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -4476,6 +4656,11 @@ core-js-pure@^3.30.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.38.1.tgz#e8534062a54b7221344884ba9b52474be495ada3" integrity sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ== +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -4656,6 +4841,61 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cypress@^13.13.3: + version "13.13.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.13.3.tgz#21ee054bb4e00b3858f2e33b4f8f4e69128470a9" + integrity sha512-hUxPrdbJXhUOTzuML+y9Av7CKoYznbD83pt8g3klgpioEha0emfx4WNIuVRx0C76r0xV2MIwAW9WYiXfVJYFQw== + dependencies: + "@cypress/request" "^3.0.1" + "@cypress/xvfb" "^1.2.4" + "@types/sinonjs__fake-timers" "8.1.1" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + buffer "^5.7.1" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.1" + commander "^6.2.1" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.4" + enquirer "^2.3.6" + eventemitter2 "6.4.7" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.1" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.8" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + process "^0.11.10" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + semver "^7.5.3" + supports-color "^8.1.1" + tmp "~0.2.3" + untildify "^4.0.0" + yauzl "^2.10.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + data-urls@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" @@ -4692,6 +4932,16 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +dayjs@^1.10.4: + version "1.11.12" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" + integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg== + +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4706,6 +4956,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" @@ -4977,11 +5234,24 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5019,6 +5289,13 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + endent@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/endent/-/endent-2.1.0.tgz#5aaba698fb569e5e18e69e1ff7a28ff35373cd88" @@ -5036,6 +5313,14 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enquirer@^2.3.6: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -5275,6 +5560,13 @@ eslint-plugin-compat@^5.0.0: lodash.memoize "^4.1.2" semver "^7.6.0" +eslint-plugin-cypress@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-3.5.0.tgz#380ef5049ad80ebeca923db69e4aa96e72fcd893" + integrity sha512-JZQ6XnBTNI8h1B9M7wJSFzc48SYbh7VMMKaNTQOFa3BQlnmXPrVc4PKen8R+fpv6VleiPeej6VxloGb42zdRvw== + dependencies: + globals "^13.20.0" + eslint-plugin-jest@^28.6.0: version "28.8.0" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz#54f597b5a3295ad04ec946baa245ad02b9b2bca0" @@ -5454,6 +5746,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -5464,6 +5761,21 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -5494,6 +5806,13 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -5547,6 +5866,32 @@ express@^4.17.3, express@^4.19.2: utils-merge "1.0.1" vary "~1.1.2" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -5621,6 +5966,20 @@ fd-package-json@^1.2.0: dependencies: walk-up-path "^3.0.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -5781,6 +6140,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + fork-ts-checker-webpack-plugin@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz#dae45dfe7298aa5d553e2580096ced79b6179504" @@ -5826,6 +6190,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -5854,6 +6227,16 @@ fs-extra@^11.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -5927,6 +6310,13 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -5946,6 +6336,20 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + giget@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/giget/-/giget-1.2.3.tgz#ef6845d1140e89adad595f7f3bb60aa31c672cb6" @@ -6018,6 +6422,13 @@ glob@^9.3.2: minipass "^4.2.4" path-scurry "^1.6.1" +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + global-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -6039,7 +6450,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0, globals@^13.24.0: +globals@^13.19.0, globals@^13.20.0, globals@^13.24.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== @@ -6105,6 +6516,13 @@ graphql@^16.8.1: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -6216,7 +6634,7 @@ html-entities@^2.1.0, html-entities@^2.4.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== -html-escaper@^2.0.0: +html-escaper@^2.0.0, html-escaper@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== @@ -6320,6 +6738,15 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -6328,6 +6755,11 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -6372,6 +6804,11 @@ ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== +immer@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -6416,6 +6853,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -6512,6 +6954,13 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== +is-ci@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + is-core-module@^2.13.0: version "2.15.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" @@ -6581,6 +7030,14 @@ is-inside-container@^1.0.0: dependencies: is-docker "^3.0.0" +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -6618,7 +7075,7 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -6696,6 +7153,11 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.3: dependencies: which-typed-array "^1.1.14" +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -6748,6 +7210,11 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -7247,6 +7714,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + jscodeshift@^0.15.1: version "0.15.2" resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.15.2.tgz#145563860360b4819a558c75c545f39683e5a0be" @@ -7335,11 +7807,21 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -7354,6 +7836,16 @@ jsonfile@^6.0.1, jsonfile@^6.1.0: optionalDependencies: graceful-fs "^4.1.6" +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -7399,6 +7891,11 @@ launch-editor@^2.6.1: picocolors "^1.0.0" shell-quote "^1.8.1" +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -7417,6 +7914,20 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -7466,6 +7977,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -7476,7 +7992,7 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: +log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -7484,6 +8000,16 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7667,7 +8193,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -7727,7 +8253,7 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.6: +minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -7777,6 +8303,11 @@ mlly@^1.7.1: pkg-types "^1.1.1" ufo "^1.5.3" +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -7787,7 +8318,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -7907,7 +8438,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^4.0.1: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -8025,7 +8556,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -8056,6 +8587,11 @@ open@^10.0.3: is-inside-container "^1.0.0" is-wsl "^3.1.0" +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -8083,6 +8619,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== + outvariant@^1.2.1, outvariant@^1.4.0, outvariant@^1.4.2: version "1.4.3" resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873" @@ -8137,6 +8678,13 @@ p-locate@^6.0.0: dependencies: p-limit "^4.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-retry@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" @@ -8279,6 +8827,16 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + picocolors@^1.0.0, picocolors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" @@ -8289,6 +8847,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -8430,6 +8993,11 @@ prettier@^3.1.1, prettier@^3.3.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -8496,6 +9064,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -8506,6 +9079,14 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -8521,6 +9102,13 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +qs@6.10.4: + version "6.10.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" + integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== + dependencies: + side-channel "^1.0.4" + qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -8829,6 +9417,13 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== + dependencies: + throttleit "^1.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -8907,6 +9502,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -8940,6 +9540,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -8955,7 +9562,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8969,7 +9576,7 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -9160,6 +9767,15 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -9175,6 +9791,15 @@ slash@^5.1.0: resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -9270,6 +9895,21 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sshpk@^1.14.1: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -9598,7 +10238,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -9758,6 +10398,16 @@ thingies@^1.20.0: resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== +throttleit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5" + integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ== + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -9773,6 +10423,11 @@ tinyspy@^2.2.0: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== +tmp@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -9795,7 +10450,12 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tough-cookie@^4.1.2, tough-cookie@^4.1.4: +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +tough-cookie@^4.1.2, tough-cookie@^4.1.3, tough-cookie@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -9872,7 +10532,7 @@ tslib@^1.13.0, tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -9884,11 +10544,23 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + tween-functions@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff" integrity sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA== +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -10112,6 +10784,11 @@ unplugin@^1.3.1: webpack-sources "^3.2.3" webpack-virtual-modules "^0.6.2" +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + update-browserslist-db@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" @@ -10143,6 +10820,11 @@ url@^0.11.0: punycode "^1.4.1" qs "^6.12.3" +use-immer@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/use-immer/-/use-immer-0.10.0.tgz#ed23afd424454604cb42bc112a455bddba894309" + integrity sha512-/eVwNR4TG9Tm/dd+aHYLLaI0FLfYKlkTqKMkn78Ah/EYVzWd/zJIgpkdoFEKbhQJOGo8XN7/mWrTx0exp1c+Ug== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -10193,6 +10875,15 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" @@ -10244,6 +10935,24 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== +webpack-bundle-analyzer@^4.10.2: + version "4.10.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" + integrity sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw== + dependencies: + "@discoveryjs/json-ext" "0.5.7" + acorn "^8.0.4" + acorn-walk "^8.0.0" + commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" + gzip-size "^6.0.0" + html-escaper "^2.0.2" + opener "^1.5.2" + picocolors "^1.0.0" + sirv "^2.0.3" + ws "^7.3.1" + webpack-cli@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" @@ -10576,6 +11285,11 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" +ws@^7.3.1: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + ws@^8.11.0, ws@^8.16.0, ws@^8.2.3: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" @@ -10634,6 +11348,14 @@ yargs@^17.3.1, yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"