diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..443ac965 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,39 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier"], + "parserOptions": { + "project": "./tsconfig.json", + "createDefaultProgram": true + }, + "env": { + // 전역객체를 eslint가 인식하는 구간 + "browser": true, // document나 window 인식되게 함 + "node": true, + "es6": true + }, + "ignorePatterns": ["node_modules/"], // eslint 미적용될 폴더나 파일 명시 + "extends": [ + "airbnb", + "airbnb-typescript", + "airbnb/hooks", + "next/core-web-vitals", + "plugin:@typescript-eslint/recommended", // ts 권장 + "plugin:prettier/recommended", // eslint의 포매팅을 prettier로 사용. + "prettier" // eslint-config-prettier prettier와 중복된 eslint 규칙 제거 + ], + "rules": { + "react/react-in-jsx-scope": "off", // react 17부턴 import 안해도돼서 기능 끔 + // 경고표시, 파일 확장자를 .ts나 .tsx 모두 허용함 + "react/jsx-filename-extension": ["warn", { "extensions": [".ts", ".tsx"] }], + "no-useless-catch": "off", // 불필요한 catch 못쓰게 하는 기능 끔 + "react/jsx-props-no-spreading": "off", + "jsx-a11y/label-has-associated-control": [ + 2, + { + "labelAttributes": ["htmlFor"] + } + ], + "no-console": "off" + } +} diff --git "a/.github/ISSUE_TEMPLATE/\342\234\205-feat--\354\235\264\354\212\210\353\252\205.md" "b/.github/ISSUE_TEMPLATE/\342\234\205-feat--\354\235\264\354\212\210\353\252\205.md" new file mode 100644 index 00000000..2accb323 --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\342\234\205-feat--\354\235\264\354\212\210\353\252\205.md" @@ -0,0 +1,17 @@ +--- +name: "✅[Feat] 이슈명" +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + +## 페이지 + +### ⚠️ Issue +- 이슈 개요를 입력해주세요. + +### ✏️ ToDoList +- [ ] 할 일1 +- [ ] 할 일2 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..336ffc5a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## 페이지 + + +## ✨ 작업 내용 + + +## ❗️ 참고 사항 + + + +## 📸 스크린샷(선택) + + +## 📚 레퍼런스 (또는 새로 알게 된 내용) 혹은 궁금한 사항들 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..00bba9bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..381a8ad0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,27 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "auto", + "htmlWhitespaceSensitivity": "css", + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "vueIndentScriptAndStyle": true, + "requirePragma": false, + "insertPragma": false, + "overrides": [ + { + "files": "*.json", + "options": { + "printWidth": 200 + } + } + ] + } \ No newline at end of file diff --git a/@types/types.ts b/@types/types.ts new file mode 100644 index 00000000..3032b3f7 --- /dev/null +++ b/@types/types.ts @@ -0,0 +1,46 @@ +export interface User { + id: string; + password: string; + name: string; + picture: string; + chats: string[]; // chat id만 속합니다. +} + +export interface Chat { + id: string; + name: string; + isPrivate: boolean; + users: ChatUser[]; + latestMessage: Message; // message 객체가 속합니다. + updatedAt: Date; +} + +export interface ChatUser { + id: string; + username: string; + picture: string; +} + +export interface Message { + id: string; + text: string; + userId: string; + createdAt: Date; +} + +export interface JoinersData { + users: string[]; // 참여자들 id + joiners: string[]; // 새로운 참여자 id +} + +export interface LeaverData { + users: string[]; // 참여자들 id + leaver: string; // 나간 사용자 id +} + +export interface IsValidAuth { + authData: { + auth: boolean; + user: User; + }; +} diff --git a/@types/user.ts b/@types/user.ts new file mode 100644 index 00000000..c36bd823 --- /dev/null +++ b/@types/user.ts @@ -0,0 +1,22 @@ +export interface ClientRefresh { + accessToken: string; +} + +export interface ClientAccessToken { + refreshToken: string; +} + +export interface UserId { + id: string; +} + +export interface UserPW { + password: string; +} + +export interface UserLogin extends UserId, UserPW {} + +export interface UserLoginResponse { + accessToken: string; + refreshToken: string; +} diff --git a/README.md b/README.md index 47dda551..6e57768c 100644 --- a/README.md +++ b/README.md @@ -1,1040 +1,471 @@ -# 🍋 소켓 기반 채팅앱 +

+ Talkhaja logo +

+ +###

함께 나누어 더 즐거운 여행 수다, Talk하자!

+ +###

Talkhaja에서 유저들과 함께 여러분들의 여정을 공유하고 즐기세요.

+ +

+ + Talkhaja Link + + + 배포 레포 + +
+

+ +### 🔐 테스트 계정 +>ID: test5 +>PW: 11111 + +### 🗓️ 개발 기간: `2주(13일) 23.11.06 ~ 23.11.16` + +## :clap: Contributors + + + + + + + + + + + + + + + + + + +
avataravataravataravataravataravatar
서지수김지민박준규어준혁윤지영구영표 멘토님
+ +## 🛢️ Stack + +### 🛠️ Tools + +

+ + + +

+ +### 🤝 Collaboration + +

+ + + + +

+ +### 🎨 Frontend Stacks + +

+ + + +
+ + + + + +

+ +### ⚙️ Backend Stacks + +

+ +

+ +## 💾 Git Branches + +- main: 배포용 브랜치 +- develop : 개발용 브랜치 +- TALK 개별 개발용 브랜치 + +## ✅ 요구 사항 +### 필수 구현 사항 + +✅ `useState`, `useReducer`를 활용한 상태 관리 구현
+✅ `Sass` 또는 `styled-component`를 활용한 스타일 구현
+✅ `react` 상태를 통한 CRUD 구현
+✅ 상태에 따라 달라지는 스타일 구현
+✅ `custom hook`을 통한 비동기 처리 구현
+✅ 유저인증 시스템(로그인, 회원가입) 구현
+✅ `jwt`등의 유저 인증 시스템 (로그인, 회원가입 기능)
+✅ 소켓을 이용한 채팅 구현
+ +### 선택 구현 사항 + +✅ `Next.js`를 활용한 서버 사이드 렌더링 구현
+✅ `typescript`를 활용한 앱 구현 + + +## :computer: 커밋 컨벤션 + +| Feat | 새로운 기능 추가 | +| --- | --- | +| Design | CSS 등 사용자 UI 디자인 변경 | +| Fix | 버그 수정 | +| Docs | 문서 수정 | +| Style | 코드 포맷팅, 세미콜론 누락 | +| Refactor | 코드 리팩토링 | +| Test | 테스트 코드, 리팩토링 테스트코드 추가 | +| Chore | 빌드 업무 수정, 패키지 매니저 수정 | +| Comment | 필요한 주석 추가 및 변경 | +| Rename | 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우 | +| Remove | 파일을 삭제하는 작업만 수행한 경우 | + +## :file_folder: 폴더 구조 + ``` + 📦Talkhaja + ┣ 📂@types + ┃ ┣ 📜types.ts + ┃ ┗ 📜user.ts + ┣ 📂apis + ┃ ┣ 📜Auth.ts + ┃ ┣ ... + ┃ ┗ 📜userListAPI.ts + ┣ 📂components + ┃ ┣ 📂Chat + ┃ ┃ ┣ 📂ChatLoading + ┃ ┃ ┃ ┣ 📜ChatLoading.module.scss + ┃ ┃ ┃ ┗ 📜ChatLoading.tsx + ┃ ┃ ┣ ... + ┃ ┃ ┗ 📜index.ts + ┃ ┣ 📂Common + ┃ ┃ ┣ 📂Header + ┃ ┃ ┃ ┣ 📜Header.module.scss + ┃ ┃ ┃ ┗ 📜Header.tsx + ┃ ┃ ┣ ... + ┃ ┃ ┗ 📜index.ts + ┣ 📂constants + ┃ ┗ 📜userLoinState.ts + ┣ 📂hooks + ┃ ┣ 📜useConnectServerSocket.tsx + ┃ ┗ 📜useOnClickOustside.tsx + ┣ 📂pages + ┃ ┣ 📂chat + ┃ ┃ ┗ 📜[chatId].tsx + ┃ ┣ 📂chat-list + ┃ ┃ ┣ 📜all.tsx + ┃ ┃ ┗ 📜my.tsx + ┃ ┣ 📜_app.tsx + ┃ ┣ 📜_document.tsx + ┃ ┣ 📜index.tsx + ┃ ┣ 📜login.tsx + ┃ ┣ 📜mypage.tsx + ┃ ┗ 📜signup.tsx + ┣ 📂public + ┃ ┣ 📂fonts + ┃ ┃ ┣ 📜NotoSansKR-Bold.ttf + ┃ ┃ ┣ ... + ┃ ┃ ┗ 📜fonts.css + ┃ ┣ 📂images + ┃ ┃ ┣ 📜Spin-1s-200px.gif + ┃ ┃ ┣ 📜Talkhaja.svg + ┃ ┃ ┗ 📜logo.png + ┃ ┣ 📜favicon.ico + ┃ ┣ 📜next.svg + ┃ ┗ 📜vercel.svg + ┣ 📂stores + ┃ ┗ 📂atoms + ┃ ┃ ┣ 📜nav.atoms.ts + ┃ ┃ ┣ 📜user.atoms.ts + ┃ ┃ ┗ 📜userTokenState.ts + ┣ 📂styles + ┃ ┣ 📜_mixin.scss + ┃ ┣ 📜_variables.scss + ┃ ┣ 📜fonts.scss + ┃ ┣ 📜index.scss + ┃ ┗ 📜normalize.scss + ┣ 📂utils + ┃ ┣ 📜authorizeFetch.ts + ┃ ┣ ... + ┃ ┗ 📜tokenManager.ts + ┣ 📜.env + ┣ 📜.eslintrc.json + ┣ 📜.gitignore + ┣ 📜.prettierrc + ┣ 📜README.md + ┣ 📜next-env.d.ts + ┣ 📜next.config.js + ┣ 📜package-lock.json + ┣ 📜package.json + ┗ 📜tsconfig.json + ``` + +## 💭 개인별 작업 내용 +
+서지수 + +## 작업 세부내용 +- 팀장 +- 채팅방 목록 조회 및 숙소, 유저와의 채팅 구분 +- 채팅방 참여 기능 +- 소켓 연결을 통한 새로 생성된 채팅방 실시간 조회 + +## 회고 + +팀장을 맡게되면서 많은 부담과 걱정이 있었다. 하지만 이번 프로젝트의 팀장 경험은 나에게 많은 의미가 있었다. 실력 부족, 일정 관리, 역할 분배 등의 걱정이 있었는데 실력 부족은 프로젝트를 진행하면서 공부, 팀원들과의 공유를 통해서 극복할 수 있었고 일정 관리, 역할 분배는 노션, 지라등의 협업 툴을 이용해 팀원들의 작업 상황, 남은 작업 파악, 각자의 진행 상황에 따른 잔업 분배 등을 효율적으로 할 수 있었고 다음 프로젝트에서도 팀원으로서 팀장 및 다른 팀원에게 나의 상황 공유을 잘 해야겠다고 생각했다. + +저번 프로젝트에서 가상화폐 데이터 소켓 연결 경험이 있어서 이번 채팅 소켓 연결과 수월할 것이라고 생각했는데 소켓 연결 후 일방적으로 데이터를 받기만 했던 전 작업과는 다르게 채팅 소켓 연결은 쌍방 소통이 필요한 작업이라 차이가 있었다. 소켓 연결 후에 입/퇴장, 메세지 수신/발신 등의 작업이 있어서 다른 방식의 소켓 연결 작업도 경험할 수 있었다. +또한, API의 에러 처리 및 에러 코드가 상세하게 구분되어있지 않아서 팀원들과 함께 개발자 도구의 네트워크 탭을 분석할 일이 많았는데 이 경험을 통해서 앞으로 백엑드에 무작정 '안돼요...'하기 보다는 네트워크 탭을 분석해서 원인을 찾는 방법을 알게 되었다. +이번 프로젝트을 통해서 이전에 Next 경험이 있었지만 작업에서 단순 Next의 문법만 따랐을 뿐이지 Next의 기능은 전혀 사용하지 않았다는 것을 알게되었고 앞으로 SSR에 대해서 공부해야겠다고 생각했다. + +마지막으로 팀원 한분한분에 대해서 회고 및 감사를 적었지만 리드미가 날라가는 바람에... 따로 전달하기로 하고 여기서는 팀원 모두에게 감사을 전합니다^^ + + +
+ +
+김지민 + +## :thought_balloon: 채팅 주요 기능 + +| 소켓 연결, 채팅 보내기, 채팅 받기 이전 채팅 가져오기 | 채팅 나가기 | +| :-------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | +| ![chatting](https://github.com/Toy2-team10/Talkhaja/assets/65649035/8d137c4a-2935-47d8-a07d-6c864546ac40) | ![chat_out](https://github.com/Toy2-team10/Talkhaja/assets/65649035/9fb496ad-8287-428e-addf-c5772d06d7f0) | +| 처음 채팅방에 들어오면 이전 대화 목록들을 모두 받습니다. 소켓 연결로 실시간 채팅이 가능합니다 | 채탱방 나가기 버튼을 누르면 해당 방에서 나가지고 목록으로 돌아갑니다 | -주어진 API와 소켓을 분석해 어떤 프로젝트를 진행/완성할 것인지 팀 단위로 자유롭게 결정하고 만들어보세요. -과제 수행 및 리뷰 기간은 별도 공지를 참고하세요! +## :art: 디자인 -## 과제 수행 및 제출 방법 +| 헤더 | 채팅방 목록 | +| :-------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ![header_1](https://github.com/Toy2-team10/Talkhaja/assets/65649035/c5ce2ae9-4c24-48bb-a4c1-36414e8f23d5) | ![chat_list_style](https://github.com/Toy2-team10/Talkhaja/assets/65649035/9292d052-0d7f-4cfd-9778-73720bcbdcaa) | +| 스크롤 위치에 따라 헤더의 크기가 달라지게 구현하였습니다. | 전체적인 채팅방목록을 디자인하고 목록을 호버하면 해당 채팅방의 배경색을 바꿔주었습니다. 또한 채팅 더하기 버튼을 누르면 모달창이 뜨도록 하였습니다. 모달창에서는 채팅 제목을 15자 이상으로 제한하였습니다. | -``` -Y_FE_Toy2_{팀명} +## :bomb: 트러블 슈팅 -E.g, Y_FE_Toy2_GYOHEON -``` +#### :x:오류 -1. 현재 저장소를 로컬에 클론(Clone)합니다. -1. 자신의 팀명으로 브랜치를 생성합니다.(구분 가능하도록 팀명을 꼭 파스칼케이스로 표시하세요, `git branch Y_FE_Toy2_Team13`) -1. 자신의 팀명 브랜치에서 과제를 수행합니다. -1. 과제 수행이 완료되면, 자신의 팀명 브랜치를 원격 저장소에 푸시(Push)합니다.(`main` 브랜치에 푸시하지 않도록 꼭 주의하세요, `git push origin Y_FE_Toy2_Team13`) -1. 저장소에서 `main` 브랜치를 대상으로 Pull Request 생성하면, 과제 제출이 완료됩니다!(E.g, `main` <== `Y_FE_Toy2_Team13`) -1. Pull Request 링크를 LMS로도 제출해 주셔야 합니다. -1. main 혹은 다른 사람의 브랜치로 절대 병합하지 않도록 주의하세요! -1. Pull Request에서 보이는 설명을 다른 사람들이 이해하기 쉽도록 꼼꼼하게 작성하세요! - -- 과제 수행 및 제출 과정에서 문제가 발생한 경우, 바로 담당 멘토나 강사에게 얘기하세요! - -- 백엔드 서버에 문제가 생겼을 경우, 바로 슬랙의 GyoHeon Lee에게 연락하세요! - -## 필수 구현 사항 -- [ ] `useState` 또는 `useReducer`를 활용한 상태 관리 구현 -- [ ] `Sass`, `styled-component`, `emotion`, `Chakra UI`, `tailwind CSS` 등을 활용한 스타일 구현 -- [ ] `react` 상태를 통한 CRUD 구현 -- [ ] 상태에 따라 달라지는 스타일 구현 -- [ ] `custom hook`을 통한 비동기 처리 구현 -- [ ] 유저인증 시스템(로그인, 회원가입) 구현 -- [ ] `jwt`등의 유저 인증 시스템 (로그인, 회원가입 기능) -- [ ] 소켓을 이용한 채팅 구현 - -## 선택 구현 사항 -- [ ] `Next.js`를 활용한 서버 사이드 렌더링 구현 -- [ ] `typescript`를 활용한 앱 구현 -- [ ] `storybook`을 활용한 디자인 시스템 구현 -- [ ] `jest`를 활용한 단위 테스트 구현 - -## 추가 사항 -- api들의 응답 데이터들을 일부러 파편화 해두었습니다! -- api들 간의 데이터를 조합하여 이상적인 구조를 만들어보세요. - -## 예시 프로젝트 - -![private-messaging-part-1-chat-ab610e9e03738ad37f7b0fb55c771087](https://github.com/KDT1-FE/Y_FE_Toy2/assets/66263916/c5247dde-2ca6-4285-a60e-8dcf23326d0e) - -## API 사용법 - -- 모든 network 요청(Request) `headers`에 아래 정보가 꼭 포함돼야 합니다! -- serverId는 팀마다 개별 전달됩니다. -- 확인할 수 없는 사용자나 팀의 DB 정보는 임의로 삭제될 수 있습니다! - -```json -{ - "content-type": "application/json", - "serverId": "nREmPe9B", -} -``` +Duplicate atom key "userIdState". This is a FATAL ERROR in production. But it is safe to ignore this warning if it occurred because of +hot module replacement. -## 기본 데이터 구조 -### user -```ts -interface User { - id: string; - password: string; - name: string; - picture: string; - chats: string[]; // chat id만 속합니다. -} -``` -### chat -```ts -interface Chat { - id: string; - name: string; - isPrivate: boolean; - users: string[]; - messages: Message[]; // message 객체가 속합니다. - - updatedAt: Date; -} -``` -### message -```ts -interface Message { - id: string; - text: string; - userId: string; - - createdAt: Date; -} -``` -## 회원 +분명 atom은 하나인데 자꾸 이런 오류가 떴습니다. +찾아보니 Next.js에서 build를 하면 atom을 저장해두는데 이것 때문에 두개로 중복된다고 Next.js에서 판단한다는 유명한 오류였습니다.;; -### 회원가입 +#### :heavy_check_mark: 해결 -사용자가 `id`에 종속되어 회원가입합니다. +RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false; +.App.js 파일 상단에 추가하니 오류가 사라졌습니다! -- 사용자 비밀번호는 암호화해 저장합니다. -- 프로필 이미지는 url or base64 형식이어야 합니다. -- 프로필 이미지는 1MB 이하여야 합니다. +-- -```curl -curl https://fastcampus-chat.net/signup - \ -X 'POST' -``` +#### :x:오류 -요청 데이터 타입 및 예시: +socket을 두번씩 생성되는 오류가 있었습니다. 이 때문에 소켓 connect가 제대로 이루어지지 않았습니다. -```ts -interface RequestBody { - id: string // 사용자 아이디 (필수!, 영어와 숫자만) - password: string // 사용자 비밀번호, 5자 이상 (필수!) - name: string // 사용자 이름, 20자 이하 (필수!) - picture?: string // 사용자 이미지(url or base64, under 1MB) -} -``` +#### :heavy_check_mark: 해결 -```json -{ - "id": "abcd", - "password": "********", - "name": "GyoHeon", - "picture": "https://avatars.githubusercontent.com/u/66263916?v=4" -} -``` +next.config.js 파일에서 reactStrictMode: false 를 해주었습니다. +react에서는 reactStrictMode: true이면 render가 강제적으로 두번씩 된다고 합니다! +또한 무한 소켓 연결을 막기 위해 useMemo를 사용하여 소켓을 생성해주었습니다 -응답 데이터 타입 및 예시: +-- -```ts -interface ResponseValue { - message: title -} -``` +#### :x:오류 -```json -{ - "message": "User created" -} -``` +socke을 connect 했음에도 불구하고 fetch-messages가 서버로 전송되지 않는 에러 -### id 중복 체크 +#### :heavy_check_mark: 해결 -`id` 중복 체크를 합니다. +setTimeout을 사용해 소켓에 요청이 들어갈때까지 요청을 보냈습니다. -```curl -curl https://fastcampus-chat.net/check/id - \ -X 'POST' -``` +-- -요청 데이터 타입 및 예시: +#### :x:오류 -```ts -interface RequestBody { - id: string // 사용자 아이디 (필수!, 영어와 숫자만) -} -``` +채팅방에서 text를 작성해 onChange 이벤트가 발생하면 상관없는 userId API를 호출하는 오류 -```json -{ - "id": "abcd", -} -``` +#### :heavy_check_mark: 해결 -응답 데이터 타입 및 예시: +userId API를 useEffect로 감싸서 의존성 배열에 chatId로만 변경하였습니다. -```ts -interface ResponseValue { - isDuplicated: boolean -} -``` +-- -```json -{ - "isDuplicated": false -} -``` +#### :x:오류 -### 로그인 +Image is missing required "src" property: -- 발급된 `accessToken`은 7일 후 만료됩니다. +#### :heavy_check_mark: 해결 -```curl -curl https://fastcampus-chat.net/login - \ -X 'POST' -``` +소켓에서 받아오는 대화정보는 userId만 받아와서 해당 userID의 정보를 API요청으로 받아줄때 +유저 이미지를 useState('')값으로 초기화를 했더니 발생한 오류였읍니다. +src 에userImage의 경로가 들어가긴 하지만 Nextjs에서 제공하는 Image태그는 src값을 절대 빈값으로 받지않아 발생는 오류였습니다. 따라서 response로 받아오는 이미지 url값이 있을때만 해당 Image 태그를 반환하게 수정하였더니 해결되었습니다 -요청 데이터 타입 및 예시: +## 회고 -```ts -interface RequestBody { - id: string // 사용자 아이디 (필수!) - password: string // 사용자 비밀번호 (필수!) -} -``` +react에서 소켓 연결은 처음이라 정말 많은 우여곡절이 있었다.:sweat_drops: 소켓 연결 시도하면서 너무 막막했지만 팀원들 덕분에 다같이 해결할 수 있었다 + 멘토님 :fire::fire:
+진짜 채팅 기능 구현 못할 것 같았는데 해냈다..도와주신 팀원분들 모두 감사합니다!:+1::+1:
+scss를 처음 사용해보았는데 생각보다 편리해서 좋았다 css를 상속할 수 있다는게 진짜 편리한 것 같다.
+axios를 사용해서 API 연결했는데 처음에는 에러 코드가 너무 세분화 되어있지 않아서 불편했는데 덕분에 네트워크 탭을 마스터 한 것 같아서 뿌듯하다..이제는 어떠한 네트워크 오류가 발생해도 해결할 수 있을 듯한 느낌:+1:
+어찌보면 간단한 기능을 구현하는 거라고 생각했는데 생각보다 시간이 걸렸고 꽤나 어려움을 느꼈다.. 그리고 이번에도 Next.js를 제대로 활용하지 못한 느낌이라 아쉬웠다.. +소켓 연결에 시간을 많이 뺏겨 SSR을 제대로 공부하고 사용할 시간적 여유가 없었어서 아쉬웠다..! +그래도 협업하는 법을 다시 한 번 알게되었고 또, 처음으로 zira를 사용해볼 수 있어서 좋았다! +하지만 아무래도 공통 컴포넌트를 개인적으로 작업하는 건 한계가 있는 것 같아서 다음 프로젝트에서는 꼭 storybook을 사용해 봐야겠다! -```json -{ - "id": "abcd", - "password": "********" -} -``` - -응답 데이터 타입 및 예시: - -```ts -interface ResponseValue { - accessToken: string // 사용자 접근 토큰 - refreshToken: string // access token 발급용 토큰 -} -``` +
-```json -{ - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)", - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)" -} -``` +
+박준규 -### 인증 확인 +## 수행 역할 -`id` 중복 체크를 합니다. +- 채팅방 UI 구성 및 컴포넌트 퍼블리싱 +- 채팅방 유저 출입 알림기능 구현 +- 실시간 채팅참여 인원 조회기능 Dropdown Menu 구현 -```curl -curl https://fastcampus-chat.net/auth/me - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` +| 채팅방 입장 시 알림 메세지 | 채팅방 퇴장 시 알림 메세지 | +| :-----------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------: | +| ![entryNotice](https://github.com/Toy2-team10/Talkhaja/assets/122848687/ddae2d89-460a-4f52-8b69-703bbbcfe0b9) | ![exitNotice](https://github.com/Toy2-team10/Talkhaja/assets/122848687/316b8380-3689-4aec-b6bf-2f434a87711c) | -요청 데이터 타입 및 예시: -- 없음 +| 채팅 참여 인원 발생시 Dropdown Menu | 채팅이탈 인원 발생시 Dropdown Menu | +| :----------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | +| ![entryModal](https://github.com/Toy2-team10/Talkhaja/assets/122848687/113beff4-d438-4e87-b778-98262a6b6750) | ![exitModal](https://github.com/Toy2-team10/Talkhaja/assets/122848687/de0cc274-0ac3-4162-b0bf-90b9d00cf663) | -응답 데이터 타입 및 예시: +## 발생했었던 이슈 -```ts -interface ResponseValue { - auth: boolean; - user?: User; -} +- 입, 퇴장 메세지 기능을 구현하는 중 다른사람이 퇴장했는데도 계속해서 똑같은 사람의 이름이 표시되는 오류가 발생했었다. -interface User { - id: string; - name: string; - picture: string; -} -``` +```tsx +socket.on('join', (messageObject: JoinersData) => { + console.log(messageObject, '채팅방 입장'); + setJoiners(prevJoiners => [...prevJoiners, ...messageObject.joiners]); +}); -```json -{ - "auth": true, - "user": { - "id": "test1", - "name": "abcde", - "picture": "https://avatars.githubusercontent.com/u/42333366?v=4" - } -} +socket.on('leave', (messageObject: LeaverData) => { + console.log(messageObject, '채팅방 퇴장'); + setLeavers(prevLeavers => [...prevLeavers, messageObject.leaver]); +}); ``` -### 토큰 재발급 +위와 같이 소켓을 통해 받아온 데이터였는데, 초반에 `Join`을 통해 받아오는 데이터는 `leave`의 데이터들과 달리 api에 불필요한 더미 데이터들이 같이 전송되어 필요한 부분만 추출하여 사용함에 있어서 애를 먹기도 했었지만 데이터는 성공적으로 받아 왔는데, 원인을 알고나니 스스로 기본기가 정말 많이 부족함을 느꼈다.. -```curl -curl https://fastcampus-chat.net/refresh - \ -X 'POST' -``` - -요청 데이터 타입 및 예시: +```tsx +useEffect(() => { + if (enterName) { + setShowEntryNotice(true); -```ts -interface RequestBody { - refreshToken: string // access token 발급용 토큰 -} -``` - -```json -{ - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)" -} -``` - -응답 데이터 타입 및 예시: - -```ts -interface ResponseValue { - accessToken: string // 사용자 접근 토큰 -} -``` - -```json -{ - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(생략)", -} -``` - -### 사용자 정보 수정 - -- 프로필 이미지는 url or base64 형식이어야 합니다. -- 프로필 이미지는 1MB 이하여야 합니다. - -```curl -curl https://fastcampus-chat.net/user - \ -X 'PATCH' - \ -H 'Authorization: Bearer ' -``` - -요청 데이터 타입 및 예시: - -```ts -interface RequestBody { - name?: string // 새로운 표시 이름 - picture?: string // 사용자 프로필 이미지(url or base64) -} -``` - -```json -{ - "name": "abcde", - "picture": "https://avatars.githubusercontent.com/u/42333366?v=4" -} -``` - -응답 데이터 타입 및 예시: - -```ts -interface ResponseValue { - messgae: string -} -``` - -```json -{ - "message": "User updated" -} -``` - -## 채팅 -### 특정 유저 조회 -- 특정 유저를 조회합니다. -```curl -curl https://fastcampus-chat.net/user?userId=${userId} - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` -요청 데이터 타입 및 예시: -- 없음 -- 조회하고 싶은 id는 query string으로 사용합니다. - -응답 데이터 타입 및 예시: -```ts -type ResponseValue = { - user: User; -} - -interface User { - id: string; - name: string; - picture: string; -} -``` - -```json -{ - "user": { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" + const entryTimer = setTimeout(() => { + setShowEntryNotice(false); + setEnterName(''); // 이부분.. + }, 3000); + return () => clearTimeout(entryTimer); } -} -``` +}, [enterName]); -### 모든 유저 조회 -- 현재 존재하는 모든 유저를 조회합니다. -```curl -curl https://fastcampus-chat.net/users - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` -요청 데이터 타입 및 예시: -- 없음 - -응답 데이터 타입 및 예시: -```ts -type ResponseValue = User[] - -interface User { - id: string; - name: string; - picture: string; -} -``` +useEffect(() => { + if (exitName) { + setShowExitNotice(true); -```json -[ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } -] -``` - -### 채팅 생성하기 - -```curl -curl https://fastcampus-chat.net/chat - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -요청 데이터 타입 및 예시: -```ts -interface RequestBody{ - name: string, // chat 이름 - users: string[], // 참가자들 id(자신 미포함) - isPrivate?: boolean // 공개 비공개 -} -``` - -```json -{ - "name": "test chat", - "users": ["user1", "user2"] -} -``` - -응답 데이터 타입 및 예시: -```ts -interface ResponseValue { - id: string, - name: string, - users: User[], // 자신을 포함한 참가자들 정보 - isPrivate: boolean, - updatedAt: Date -} - -interface User { - id: string; - name: string; - picture: string; -} -``` - -```json -{ - "id": "fasgadsfdsghssdlsdafasd", - "name": "test chat", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": false, - "updatedAt": "2023-11-01T08:23:39.850Z" -} -``` - -### 특정 채팅 조회 -- 특정 id의 채팅을 조회합니다. -- isPrivate: true인 채팅방은 해당 채팅방 참가자만 볼 수 있습니다. - -```curl -curl https://fastcampus-chat.net/chat/only?chatId=${chatId} - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` - -요청 데이터 타입 및 예시: -- 없음 - -응답 데이터 타입 및 예시: -```ts -interface ResponseValue { - chat: Chat; -} - -interface Chat { - id: string; - name: string; - users: User[]; // 속한 유저 정보 - isPrivate: boolean; - latestMessage: Message | null; - updatedAt: Date; -} - -interface User { - id: string; - name: string; - picture: string; -} - -interface Message { - id: string; - text: string; - userId: string; - createAt: Date; -} -``` + const exitTimer = setTimeout(() => { + setShowExitNotice(false); + setExitName(''); // 이부분.. + }, 3000); -```json -{ - chat: { - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "name": "chat room 1", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": false, - "updatedAt": "2023-10-31T13:18:38.216Z", - "latestMessage": null + return () => clearTimeout(exitTimer); } -} +}, [exitName]); ``` -### 모든 채팅 조회 -- 현재 존재하는 모든 채팅을 조회합니다. -- isPrivate: true인 채팅방은 보이지 않습니다. +useState를 사용함에 있어서 사용이 끝난 직후 state값을 초기화 해주는 작업을 제외하고 작업을 진행하다 보니, 예상과는 다른 값들이 도출되어 문제가 발생했던 것이다. 이후 값을 초기화해주는 코드를 넣고 작업을 진행했는데, 다행히 정상동작함을 확인할 수 있었다. -```curl -curl https://fastcampus-chat.net/chat/all - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` +이 이후에도 채팅방에서 특정 유저를 조회하여 해당 유저의 사진정보와 이름 등을 받아오는데 원인모를 문제때문에 6시간이 넘도록 앉아있기도 했었는데, 결국은 `api를 요청하는 주소의 오타`때문인 것을 알고 난 뒤에는 정말이지 그렇게 허무할 수 없었다..ㅠㅠ -요청 데이터 타입 및 예시: -- 없음 - -응답 데이터 타입 및 예시: -```ts -type ResponseValue = Chat[] - -interface Chat { - id: string; - name: string; - users: User[]; // 속한 유저 정보 - isPrivate: boolean; - latestMessage: Message | null; - updatedAt: Date; -} - -interface User { - id: string; - name: string; - picture: string; -} - -interface Message { - id: string; - text: string; - userId: string; - createAt: Date; -} -``` +다행히 그렇게 큰 이슈는 아니었지만, +`console.log를 찍어서 잘 보내고 잘 받아오는지 확인하기`, +`외부 데이터를 사용할때는 오타가 있는지 확인하기` 등 개발을 함에 있어서 항상 내가 올바르게 하고 있는지 수시로 확인 할 필요가 있음을 뼈저리게 느끼게 되었다. -```json -[ - { - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "name": "chat room 1", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": false, - "updatedAt": "2023-10-31T13:18:38.216Z", - "latestMessage": null - }, - { - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8edj", - "name": "chat room 2", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": false, - "updatedAt": "2023-10-31T15:18:38.216Z", - "latestMessage": { - "id": "8f7f67bb-f1ab-4792-9678-0b8546adcb6f", - "text": "testtest444", - "userId": "test:test6", - "createdAt": "2023-11-06T11:15:50.588+00:00" - } - } -] -``` +## 회고 -### 나의 채팅 조회 -```curl -curl https://fastcampus-chat.net/chat - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` -- 내가 속한 모든 채팅을 조회합니다. -- isPrivate: true인 채팅방도 모두 보이게 됩니다. - -요청 데이터 타입 및 예시: -- 없음 - -응답 데이터 타입 및 예시: -```ts -type ResponseValue = Chat[] - -interface Chat { - id: string; - name: string; - users: User[]; // 속한 유저 id - isPrivate: boolean; - latestMessage: Message | null; - updatedAt: Date; -} - -interface User { - id: string; - name: string; - picture: string; -} - -interface Message { - id: string; - text: string; - userId: string; - createAt: Date; -} -``` +이번 기간동안 가급적 많은 도전과 시도를 통해서 많은 공부를 해야했던 좋은 기회였음에도 불구하고, +그러지 못했음에 다소 아쉬움이 남았다. -```json -[ - { - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "name": "chat room 1", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": true, - "updatedAt": "2023-10-31T13:18:38.216Z", - "latestMessage": null - }, - { - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8edj", - "name": "chat room 2", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": false, - "updatedAt": "2023-10-31T15:18:38.216Z", - "latestMessage": { - "id": "8f7f67bb-f1ab-4792-9678-0b8546adcb6f", - "text": "testtest444", - "userId": "test:test6", - "createdAt": "2023-11-06T11:15:50.588+00:00" - } - } -] -``` +2주라는 짧은 기간에 Next.js나 recoil(이번 프로젝트때 한번도 사용해보진 못했지만..) socket 등의 기술들을 가지고 결과물을 만들어내는것이 쉬운 일도 아니었을 뿐더러, 특히 우리조의 API-key에 이슈가 있어 개발도중 난항을 겪기도 했었지만 +부단하게 노력하며 밤을 지새우면서까지 나를 가르쳐준 우리 조장과 팀원들 덕에 결국 프로젝트를 마무리할 수 있게 되었다. -## 채팅 참여하기 +사실 이번 프로젝트 기간동안 약간의 슬럼프가 찾아오기도 했었다. +`'지금까지 달려왔는데도 실력도 크게 늘지않고, 도통 어떻게 해야할지 잘 모르겠다'`는 생각도 자주 들었었고, +`'이런 간단한 것 조차도 구현하는데 있어서 많은 시간이 걸리는걸 보니, 나는 사실 개발에 재능이 없을지도 모르겠다'` +는 생각도 들고, `'미니 프로젝트를 진행하기 이전에 개발자를 포기해야하나'` 라고 혼자 고민을 하기도 했었다. -```curl -curl https://fastcampus-chat.net/chat/participate - \ -X 'PATCH' - \ -H 'Authorization: Bearer ' -``` +하지만 구현한 기능이 아무리 적더라도 성공적으로 구현하는데 목표를 두고 도전해보라며 격려해주시던 유영매니저님과 팀원들, 그리고 스터디그룹원들의 응원 덕분에 다시금 키보드를 잡고 오랜만에 꺼져가던 열정에 다시 숨을 불어넣었고, +목표했던 기능들의 구현을 성공적으로 마칠 수 있었다. -요청 데이터 타입 및 예시: -```ts -interface RequestBody { - chatId: string; -} -``` +어찌보면 항상 강의장에서 다른 수강생들과 함께 머리를 맞대며 공부를 이어나가는 수강생들과는 달리 4개월이 넘는 시간동안 어두컴컴한 방에 틀어박혀 작은 글씨들을 보고 있으니 몸과 마음이 지칠만도 하다. 게다가 새로운 기술을 배우는데 있어 남들보다 조금 더 천천히 내가 원하는대로 뜯어보며 배워가는 타입이다 보니, 빠르게 성장하는 다른 팀원들의 모습에 자존심도 상하고, 조바심이 나기도 했었다. -```json -{ - "chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede" -} -``` +하지만 이번 프로젝트를 말미암아 내 스스로 어떤 공부를 해야하는지, 현재 내가 어떤 상황에 놓였는지에 대해서 조금 더 진지하게 생각할 수 있었던 계기가 된 것 같다. 지금 당장은 가파른 성장을 이루지 못하더라도, 언젠간 깊은 뿌리를 내려 큰 결실을 맺을 수 있는 거목같은 개발자가 되리라고 스스로 다짐해본다. -응답 데이터 타입 및 예시: -```ts -interface ResponseValue{ - id: string; - name: string; - users: User[]; // 속한 유저 id - isPrivate: boolean; - updatedAt: Date; -} - -interface User { - id: string; - name: string; - picture: string; -} -``` +
-```json -{ - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "name": "chat room 1", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": true, - "updatedAt": "2023-10-31T13:18:38.216Z" -} -``` +
+어준혁 -## 채팅 나가기 +## 작업 내용 요약 -```curl -curl https://fastcampus-chat.net/chat/leave - \ -X 'PATCH' - \ -H 'Authorization: Bearer ' -``` +- 로그인 +- 회원가입 +- 마이페이지 +- 리다이렉트 -요청 데이터 타입 및 예시: -```ts -interface RequestBody { - chatId: string; -} -``` +## 로그인 기능 구현 -```json -{ - "chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede" -} -``` +- JWT 토큰과 쿠키를 이용한 토큰 관리(next-cookie) -응답 데이터 타입 및 예시: -```ts -interface ResponseValue { - message: string; -} -``` +## 회원가입 기능 구현 -```json -{ - "message": "Leave success" -} -``` +- 사진등록(firebase storage) +- 입력값 유효성 검사(useForm) +- -## 채팅 초대하기 +## 마이페이지 기능 구현 -```curl -curl https://fastcampus-chat.net/chat/invite - \ -X 'PATCH' - \ -H 'Authorization: Bearer ' -``` +- 유저 사진, 이름 변경 +- 로그아웃 -요청 데이터 타입 및 예시: -```ts -interface RequestBody { - chatId: string; - users: string[]; // 초대할 유저 id -} -``` +## 후기 -```json -{ - "chatId": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "users": ["user1", "user2"] -} -``` +프로젝트를 진행할 때 항상 리액트를 사용했었다. 그래서 팀에서 넥스트를 사용한다고 하였을 때 조금은 겁이 났다. 토이 프로젝트가 끝나고 한달이나 시간이 있었는데 공부를 안한 내 자신이 후회스러웠다. 프로젝트가 시작되고 공부를 하면서 구현을 하기 시작했다. 하지만 CSR에 익숙했던 나는 넥스트의 장점을 전혀 살리지 못한 코드를 작성하고 있었다. 그래서 시간이 걸리더라도 SSR기반의 코드를 짜보자고 다짐하여 getServerSideProps를 사용하면서 넥스트에 재미가 붙기 시작했다. 또 팀원들과 패스트캠퍼스 강의장에서 다같이 작업하다보니 시너지 효과가 발휘되어 개발에만 전념할 수 있었다. 이 프로젝트를 진행하고 새로운 기술에 대한 호기심이 더욱 더 생겼고 개발이 더 재밌어졌다. 이런 마음을 오랫동안 유지하고싶다. -응답 데이터 타입 및 예시: -```ts -interface ResponseValue{ - id: string; - name: string; - users: User[]; // 속한 유저 정보 - isPrivate: boolean; - updatedAt: Date; -} - -interface User { - id: string; - name: string; - picture: string; -} -``` +
-```json -{ - "id": "f189ab25-5644-4d72-bd7c-0170ee9c8ede", - "name": "chat room 1", - "users": [ - { - "id": "user1", - "name": "lgh", - "picture": "https://gravatar.com/avatar/c274467c5ef4fe381b154a20c5e7ce26?s=200&d=retro" - }, - { - "id": "user2", - "name": "ldj", - "picture": "https://gravatar.com/avatar/d94869409b4e94903723612a4f93a6f9?s=200&d=retro" - } - ], - "isPrivate": true, - "updatedAt": "2023-10-31T13:18:38.216Z" -} -``` +
+윤지영 -# Socket -- socket.io 의 사용을 추천드립니다. -- Socket 연결시에도 headers는 유지해야 합니다. -## 기본 연결 -```ts -io(`https://fastcampus-chat.net/chat?chatId=${chatId}`, - { - extraHeaders: { - Authorization: "Bearer ", - serverId: "test", - }, - }) -``` +## 작업 내용 -## emit Event(client -> server) -### example -```ts -socket.emit('message-to-server', text) -``` -### message-to-server -- 같은 방에 있는 사람들에게 메세지를 전달합니다. +| 해시태그를 통한 스크롤 이동 | 검색을 통한 데이터 필터링 | +| :----------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------: | +| ![scroll](https://github.com/developer-jyyun/testtalk/assets/131247158/6c567de5-7027-4694-8ee7-f7c8e544b1d8) | ![search__modal](https://github.com/developer-jyyun/testtalk/assets/131247158/098af386-4d70-48d9-9fa8-c9708e41e819) | -요청 데이터 -```ts -type RequestData: string; -``` -### fetch-messages -- 이전 대화 목록을 불러옵니다. -- `messages-to-client`로 데이터를 받을 수 있습니다. - -요청 데이터 -- 없음 -### users -- 접속 상태인 유저 목록을 불러옵니다. -- `users-to-client`로 데이터를 받을 수 있습니다. - -요청 데이터 -- 없음 - -## on Event(server -> client) -### example -```ts -socket.on('message-to-client', (messageObject) => { - console.log(messageObject); -}) -``` -### message-to-client -- 같은 방에 있는 사람들에게 메세지를 전달합니다. - -응답 데이터 -```ts -interface ResponseData { - id: string; - text: string; - userId: string; // 메세지를 보낸 사람의 id - createdAt: Date; -} -``` -### messages-to-client -- 이전 대화 목록을 불러옵니다. - -응답 데이터 -```ts -interface Message { - id: string; - text: string; - userId: string; // 메세지를 보낸 사람의 id - createdAt: Date; -} - -interface ResponseData { - messages: Message[]; -} -``` -### join -- 같은 방에 새로운 사람이 들어오면 모든 유저의 정보를 다시 받습니다. - -응답 데이터 -```ts -interface ResponseData { - users: string[]; // 참여자들 id - joiners: string[]; // 새로운 참여자 id -} -``` -### leave -- 같은 방에 사람이 나가면 모든 유저의 정보를 다시 받습니다. - -응답 데이터 -```ts -interface ResponseData { - users: string[]; // 참여자들 id - leaver: string; // 나간 사용자 id -} -``` +| 채팅목록 시간 포맷팅 | 숙소 정보 상세보기 모달 | +| :---------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | +| ![image](https://github.com/developer-jyyun/testtalk/assets/131247158/37a38153-bbe1-4c6d-b85d-370a666b4164) | ![MODAL](https://github.com/developer-jyyun/testtalk/assets/131247158/ef3eb661-74b3-4da9-a084-34b49b03ac4b) | -### users-to-client -- 접속 상태인 유저 목록을 불러옵니다. +#### 숙소 목록 페이지 -응답 데이터 -```ts -interface ResponseData { - user: string[]; // 참가자들 id -} -``` +- 데이터를 지역별로 필터링 하여 사용자에게 노출 +- 스크롤 시 상단 고정되는 해시태그를 통해 원하는 지역으로 스크롤 이동 +- 키워드 검색 결과를 지역별로 노출 (숙소데이터 관련 모든 키워드) +- 검색결과 유/무, 검색어 유/무에 따른 페이지/ 컴포넌트 렌더링 -## server 연결 -```ts -io(`https://fastcampus-chat.net/server`, - { - extraHeaders: { - Authorization: "Bearer ", - serverId: "test", - }, - }) -``` +#### 하단 고정 내비게이션 -## emit Event(client -> server) -### example -```ts -socket.emit('users-server') -``` -### users-server -- 같은 serverId를 사용하는 online 사용자를 불러옵니다. -- `users-server-to-client`로 데이터를 받을 수 있습니다. - -요청 데이터 -- 없음 - -## on Event(server -> client) -### example -```ts -socket.on('message-to-client', (messageObject) => { - console.log(messageObject); -}) -``` +#### 오픈 채팅 목록 시간 표시 포맷팅 -### users-server-to-client -- 같은 serverId를 사용하는 접속 상태인 유저 목록을 불러옵니다. +## 작업 중 어려웠던 점 -응답 데이터 -```ts -interface ResponseData { - user: string[]; // 참가자들 id -} -``` +제공된 api만으로는 숙박업체 유저와 일반 사용자 유저를 구분하기가 쉽지 않았는데 다행히 프로젝트 시작 후 금방 진행된 멘토님의 조언 덕분에 덜 헤매고 수월하게 작업 할 수 있었다!! +하지만 프로젝트 종료일 직전 효율적인 코드를 위해 데이터를 읽고 쓰는 방식을 변경하기로 하면서 그에 따른 코드변화가 도미노처럼 줄줄이 이어졌다. 사실 하나하나씩 차근차근 보면 별 거 아닌 것 같은데 금방 끝날 것 같던 작업이 끝나지 않으니 마음이 점점 더 조급해져 갔다. 처음부터 충분히 고민해보고 잘 설계 후에 작업 했다면 훨씬 여유롭게 프로젝트를 마칠 수 있었을 것 같아 아쉽다. 매 작업 시 느끼는 어려운 점이 설계 단계인 것 같다. 직접 코드를 짜기 전까지는 가늠이 안된다..작업 시간도 구조 설계도...더 많이 배우고 경험해봐야 할 것 같다! -### invite -- 새로운 채팅방 생성시 해당 채팅방 유저에게 채팅방 정보를 전송합니다. -- 기존 채팅방에 유저 초대시 초대된 유저에게 채팅방 정보를 전송합니다. - -응답 데이터 -```ts -interface ResponseData { - id: string; - name: string; - users: string[]; // 참여자들 id - isPrivate: boolean; - updatedAt: Date; -} -``` +## 회고 -### new-chat -- 새로운 대화방이 생긴 경우 (not private) 서버(팀에서 사용하는 serverId)의 참여자들에게 이를 전달합니다. - -응답 데이터 -```ts -interface ResponseData { - id: string; - name: string; - users: string[]; // 참여자들 id - isPrivate: boolean; - updatedAt: Date; -} -``` +급변하는 프론트엔드 세계에서 항상 새로운 라이브러리와 새로운 프레임워크들을 익혀야 한다는 점이 항상 마음의 짐처럼 쌓여 큰 부담으로 다가왔었는데 이번 프로젝트를 통해 새로운 것들을 익히는 것에 대한 부담감을 많이 줄일 수 있던 것 같다. 아무래도 다양한 것들을 많이 사용해 볼수록 각 라이브러리들의 편리함을 체감할 수 있게 되어 그런 것 같다. NEXT.js를 본격적으로 사용해 본 적은 처음이었는데 많이 낯설지 않아서 좋았다. 하지만 낯설지 않았단 점은 NEXT.js의 장점을 충분히 활용하지 못했기 때문 인 것 같다..😂 api라우트를 사용하지 못한 점이 너무 아쉽다. 또 기능별 커밋이 아닌 페이지별 작업하는 아주 나쁜 습관이 쉽게 고쳐지지 않는다😂..하루 빨리 고칠 수 있도록 노력해야겠다! 좋은 조장님과 팀원들, 그리고 멘토님을 만난 덕분에 프로젝트를 잘 마무리 할 수 있었던 것 같다. 단순히 역할을 나눠 각자의 기능 개발을 하기 보단, 작업 현황과 결과물을 보며 이 작업은 어떻게 개선되면 좋을지에 대한 피드백을 말해주셔서 특히나 많은 것들을 접하고 배울 수 있었다. 문제를 해결하기 위한 접근 방법들도 차근차근 알려주셔서 수정작업도 즐겁게 할 수 있던 것 같다. 이번 프로젝트를 되돌아 보니 도움받은것들 뿐이어서 많은 아쉬움이 남는다. 팀원들에게 도움되는 사람이 될 수 있도록 분발해야겠다🔥 + +
diff --git a/apis/Auth.ts b/apis/Auth.ts new file mode 100644 index 00000000..a7b6c989 --- /dev/null +++ b/apis/Auth.ts @@ -0,0 +1,30 @@ +import { UserLogin } from '@/@types/user'; +import { setStorage } from '@/utils/loginStorage'; +import { useRouter } from 'next/router'; +import { useSetRecoilState } from 'recoil'; +import userIdState from '@/stores/atoms/user.atoms'; +import Jwtinterceptor from './JwtInterceptor'; + +const Auth = () => { + const { instance } = Jwtinterceptor(); + const router = useRouter(); + const setUserId = useSetRecoilState(userIdState); + + // 로그인 (로그인유지) + const login = async (userLogin: UserLogin) => { + try { + const response = await instance.post('/login', userLogin); + console.log(response); + setStorage('accessToken', response.data.accessToken); + setStorage('refreshToken', response.data.refreshToken); + setUserId(userLogin.id); + router.push('/'); + } catch (error) { + console.log(error); + } + }; + + return { login }; +}; + +export default Auth; diff --git a/apis/JwtInterceptor.ts b/apis/JwtInterceptor.ts new file mode 100644 index 00000000..686af68c --- /dev/null +++ b/apis/JwtInterceptor.ts @@ -0,0 +1,19 @@ +import { getAccessToken } from '@/utils/tokenManager'; +import axios from 'axios'; + +const Jwtinterceptor = () => { + const accessToken = getAccessToken(); + const instance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_BASE_URL, + headers: { + 'Content-Type': 'application/json', + serverId: process.env.NEXT_PUBLIC_API_KEY, + withCredentials: true, + Authorization: `Bearer ${accessToken}`, + }, + }); + + return { instance }; +}; + +export default Jwtinterceptor; diff --git a/apis/axios.ts b/apis/axios.ts new file mode 100644 index 00000000..39bbf9ee --- /dev/null +++ b/apis/axios.ts @@ -0,0 +1,12 @@ +import axios from 'axios'; + +const instance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_BASE_URL, + headers: { + 'Content-Type': 'application/json', + serverId: process.env.NEXT_PUBLIC_API_KEY, + withCredentials: true, + }, +}); + +export default instance; diff --git a/apis/chatAPI.ts b/apis/chatAPI.ts new file mode 100644 index 00000000..c7985af2 --- /dev/null +++ b/apis/chatAPI.ts @@ -0,0 +1,16 @@ +import Jwtinterceptor from './JwtInterceptor'; + +const { instance } = Jwtinterceptor(); + +const chatAPI = { + // 특정 채팅방 정보조회 + getChatInfo(chatId: string) { + return instance.get(`/chat/only?chatId=${chatId}`); + }, + // 특정 유저 조회 + getUserInfo(userId: string) { + return instance.get(`/user?userId=${userId}`); + }, +}; + +export default chatAPI; diff --git a/apis/chatListAPI.ts b/apis/chatListAPI.ts new file mode 100644 index 00000000..70730ac0 --- /dev/null +++ b/apis/chatListAPI.ts @@ -0,0 +1,30 @@ +import Jwtinterceptor from './JwtInterceptor'; + +interface ICreateChatRequestBody { + name: string; // chat 이름 + users: string[]; // 참가자들 id(자신 미포함) + isPrivate?: boolean; // 공개 비공개 +} + +const { instance } = Jwtinterceptor(); + +const chatListAPI = { + // 모든 채팅 조회 + getAllChatList() { + return instance.get('/chat/all'); + }, + // 나의 채팅 조회 + getMyChatList() { + return instance.get('/chat'); + }, + // 채팅방 생성 + createChat(data: ICreateChatRequestBody) { + return instance.post('/chat', data); + }, + // 채팅 참여 + participateChat(chatId: string) { + return instance.patch('/chat/participate', { chatId }); + }, +}; + +export default chatListAPI; diff --git a/apis/userAPI.ts b/apis/userAPI.ts new file mode 100644 index 00000000..10b5b360 --- /dev/null +++ b/apis/userAPI.ts @@ -0,0 +1,12 @@ +import Jwtinterceptor from './JwtInterceptor'; + +const { instance } = Jwtinterceptor(); + +const userAPI = { + // 엑세스토큰 유효성 검사 + getTokenValid() { + return instance.get('/auth/me'); + }, +}; + +export default userAPI; diff --git a/apis/userListAPI.ts b/apis/userListAPI.ts new file mode 100644 index 00000000..124bfa23 --- /dev/null +++ b/apis/userListAPI.ts @@ -0,0 +1,12 @@ +import Jwtinterceptor from './JwtInterceptor'; + +const { instance } = Jwtinterceptor(); + +const userListAPI = { + // 모든 유저 조회 + getAllUserList() { + return instance.get('/users'); + }, +}; + +export default userListAPI; diff --git a/components/Chat/Chat.module.scss b/components/Chat/Chat.module.scss new file mode 100644 index 00000000..3652c56a --- /dev/null +++ b/components/Chat/Chat.module.scss @@ -0,0 +1,372 @@ +@import '@/styles/_variables'; +@import '@/styles/_mixin'; + +/*---------- 전체 container 스타일 ----------*/ +.container { + @include container; + + margin-top: 6rem; + margin-bottom: 8rem; + + min-height: calc(100vh - 14.1rem); +} + +/*---------- header 영역 스타일 ----------*/ +.header { + @include flex-between-row; + + position: fixed; + top: 0; + + width: 100%; + height: 6rem; + + font-size: $font-size-lg; + + border-bottom: 0.1rem solid #ff347893; + background-color: white; + + // 채팅방 이름 + .chatTitle { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + @media (min-width: 800px) { + font-size: 2rem; + } + @media (max-width: 799px) { + font-size: $font-size-lg; + } + } + + // 뒤로가기 버튼, 메뉴(속성) 버튼 + .left, + .right { + color: $light-pink; + font-size: 2.4rem; + cursor: pointer; + margin: 0 2rem; + &.right { + border: none; + background-color: white; + position: relative; + font-size: 1.8rem; + } + } +} + +// 드롭다운 메뉴 +.dropdownMenu { + width: 15rem; + + position: absolute; + top: 100%; + right: 0.1rem; + + border: 0.1rem solid #ccc; + background-color: white; + box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.15); + + padding: 1rem; + + z-index: 1000; + + ul { + list-style: none; + + padding: 0; + margin: 0; + } + p, + h6 { + margin: 0; + } + div { + padding: 1rem; + border-bottom: 0.1rem solid $brand-pink; + } + .userDiv { + max-height: 20rem; + overflow-y: auto; + } + + .counter { + text-align: left; + } + li { + padding: 0.8rem 0; + + display: flex; + justify-content: flex-start; + align-items: center; + + cursor: pointer; + + &:hover { + background-color: $light-gray; // 항목에 호버 시 배경색 변경 + } + .profileImage { + width: 3.5rem; + height: 3.5rem; + border-radius: 50%; + margin-right: 0.5rem; + } + .username { + font-size: $font-size-s; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + .exitButton { + margin: 1rem 2.44rem 0 2.44rem; + padding: 0.6rem 1rem; + font-size: $font-size-base; + justify-content: center; + align-items: center; + color: white; + background-color: $light-pink; + border: none; + outline: none; + border-radius: 0.5rem; + + cursor: pointer; + &:hover { + background-color: $brand-pink; + transition: 0.3s; + } + } +} + +/*---------- footer 영역 스타일 ----------*/ +.footer { + @include flex-center-row; + + background-color: #ebe9e9; + + width: 100%; + height: 8rem; + + position: fixed; + bottom: 0; + z-index: 999; +} + +// 메세지 입력창 +.chatInput { + border: 0.1rem solid #ffffff; + border-radius: 0.5rem; + outline: none; + + height: 5rem; + + padding: 0 1rem; + margin: 0 0.5rem; + + position: relative; + + font-size: $font-size-base; + + @media (min-width: 800px) { + width: 70rem; + font-size: $font-size-lg; + } + + @media (max-width: 799px) { + min-width: 70%; + font-size: $font-size-base; + } + + transition: border-color 0.2s ease-in-out; + + &:focus { + border-color: $brand-pink; + transition: border-color 0.2s ease-in-out; + } +} + +// 메세지 전송버튼 +.triangle_button { + @include flex-center-row; + @include button; + width: 5rem; + height: 5rem; + outline: none; + + background-color: #ffffff; + border-radius: 50%; + + cursor: pointer; + + &:after { + content: ''; + + width: 0; + height: 0; + + border-style: solid; + border-width: 1.5rem 0 1.5rem 2.5rem; + border-color: transparent transparent transparent #ff3478; /* 오른쪽 방향의 파란색 삼각형 */ + + margin-left: 0.5rem; + + transition: border-color 0.2s ease-in-out; + } + + &:active:after { + border-color: transparent transparent transparent rgb(196, 22, 80); /* 클릭 시 테두리 색상 변경 */ + } +} + +/*---------- 채팅 영역 스타일---------- */ +// 수실 메세지 스타일 (다른사람) +.otherFlex { + display: flex; + flex-direction: column; + + justify-content: flex-start; + align-items: flex-start; + padding: 1rem 0; + margin-left: 1rem; + .userInfo { + display: flex; + align-items: center; + margin-bottom: 0.5rem; + } + + .profileImage { + width: 3.5rem; + height: 3.5rem; + border-radius: 50%; + margin-right: 0.7rem; + } + + .username { + font-size: $font-size-s; + } + + .otherMessage { + display: flex; + align-items: flex-end; + justify-content: flex-start; + + padding-right: 1rem; + + span { + margin-left: 0.4rem; + font-size: 1rem; + margin-bottom: 0.4rem; + } + + .content { + /* 채팅 메시지 최대 너비 설정 */ + + @media (min-width: 800px) { + max-width: 60rem; + font-size: $font-size-lg; + } + + @media (max-width: 799px) { + max-width: 16rem; + font-size: $font-size-base; + } + background-color: #ff347879; + + padding: 1rem; + border-radius: 1.8rem; + + word-wrap: break-word; + white-space: pre-wrap; + } + } +} + +// 발신 메세지 스타일 (나) +.myFlex { + display: flex; + align-items: flex-start; + justify-content: flex-end; + + width: 100%; + + padding: 1rem 0; + + .myMessage { + display: flex; + align-items: flex-end; + justify-content: flex-end; + + padding-right: 1rem; + + span { + margin-right: 0.4rem; + font-size: 1rem; + margin-bottom: 0.4rem; + } + + .content { + @media (min-width: 800px) { + max-width: 60rem; + font-size: $font-size-lg; + } + + @media (max-width: 799px) { + max-width: 16rem; + font-size: $font-size-base; + } + + padding: 1rem; + + border-radius: 1.8rem; + border: 0.1rem solid #ff34789a; + + word-wrap: break-word; + white-space: pre-wrap; + } + } +} + +.notice { + @include flex-center-row; + + padding: 1rem 0; + width: 100%; + overflow: hidden; + + .box { + text-align: center; + + max-width: 80%; + + padding: 0.5rem 2.5rem; + + background-color: #d9d9d9; + border-radius: 40rem; + } +} + +.alert { + position: fixed; + bottom: 10rem; + left: 50%; + transform: translateX(-50%); + width: 15rem; + + text-align: center; + + z-index: 1000; + + padding: 1rem; + + border-radius: 0.5rem; + + .box { + background-color: #d9d9d949; + color: black; + + padding: 1rem; + + border-radius: 0.5rem; + } +} diff --git a/components/Chat/ChatAlert.tsx b/components/Chat/ChatAlert.tsx new file mode 100644 index 00000000..01fe6537 --- /dev/null +++ b/components/Chat/ChatAlert.tsx @@ -0,0 +1,9 @@ +import styles from './Chat.module.scss'; + +export default function ChatAlert() { + return ( +
+
내용을 입력해주세요!
+
+ ); +} diff --git a/components/Chat/ChatLoading/ChatLoading.module.scss b/components/Chat/ChatLoading/ChatLoading.module.scss new file mode 100644 index 00000000..7ad1a60d --- /dev/null +++ b/components/Chat/ChatLoading/ChatLoading.module.scss @@ -0,0 +1,18 @@ +@import '@/styles/mixin'; + +.container { + @include flex-center-col; + position: absolute; + top: 50%; + left: 50%; + z-index: 1; + transform: translate(-50%, -50%); + background-color: white; + padding: 5rem 7rem; + border-radius: 2rem; + + h3 { + word-break: keep-all; + font-size: 2rem; + } +} diff --git a/components/Chat/ChatLoading/ChatLoading.tsx b/components/Chat/ChatLoading/ChatLoading.tsx new file mode 100644 index 00000000..bdb2045b --- /dev/null +++ b/components/Chat/ChatLoading/ChatLoading.tsx @@ -0,0 +1,17 @@ +import Image from 'next/image'; +import React from 'react'; +import styles from './ChatLoading.module.scss'; + +export default function ChatLoading() { + return ( +
+

채팅방을 불러오는 중입니다..

+ loading +
+ ); +} diff --git a/components/Chat/ChatroomHeader.tsx b/components/Chat/ChatroomHeader.tsx new file mode 100644 index 00000000..ad6bf4c6 --- /dev/null +++ b/components/Chat/ChatroomHeader.tsx @@ -0,0 +1,97 @@ +import { useState } from 'react'; +import { useRouter } from 'next/router'; +import { HiArrowLongLeft } from 'react-icons/hi2'; +import { IoMdMenu } from 'react-icons/io'; +import Jwtinterceptor from '@/apis/JwtInterceptor'; +import { getAccessToken } from '@/utils/tokenManager'; +import { Chat } from '@/@types/types'; +import Image from 'next/image'; +import styles from './Chat.module.scss'; + +interface Props { + chatData: Chat; +} + +export default function ChatroomHeader({ chatData }: Props) { + const router = useRouter(); + + const { instance } = Jwtinterceptor(); + + const accessToken: string = getAccessToken(); + + const [menuOpen, setMenuOpen] = useState(false); + + const toggleMenu = () => { + setMenuOpen(!menuOpen); + }; + + const handleBackBtnClick = () => { + router.back(); + }; + + const handleOutBtnClick = async () => { + try { + await instance.patch( + '/chat/leave', + { + chatId: chatData.id, + }, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); + router.push('/chat-list/my'); + } catch (error) { + console.error(error); + } + }; + + return ( +
+
+ +
+ {chatData && ( + <> +

{chatData.name}

+ +
+ )} + + + )} + + ); +} diff --git a/components/Chat/EntryNotice.tsx b/components/Chat/EntryNotice.tsx new file mode 100644 index 00000000..434d8b88 --- /dev/null +++ b/components/Chat/EntryNotice.tsx @@ -0,0 +1,9 @@ +import styles from './Chat.module.scss'; + +export default function EntryNotice({ joiner }: { joiner: string }) { + return ( +
+
{joiner} 님이 입장하셨습니다.
+
+ ); +} diff --git a/components/Chat/ExitNotice.tsx b/components/Chat/ExitNotice.tsx new file mode 100644 index 00000000..b1006678 --- /dev/null +++ b/components/Chat/ExitNotice.tsx @@ -0,0 +1,9 @@ +import styles from './Chat.module.scss'; + +export default function ExitNotice({ leaver }: { leaver: string }) { + return ( +
+
{leaver} 님이 퇴장하셨습니다.
+
+ ); +} diff --git a/components/Chat/MyMessage.tsx b/components/Chat/MyMessage.tsx new file mode 100644 index 00000000..d637381c --- /dev/null +++ b/components/Chat/MyMessage.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { formattingTime, todayDate } from '@/utils/formattedTimeData'; +import { Message } from '@/@types/types'; +import styles from './Chat.module.scss'; + +export default function MyMessage({ msg }: { msg: Message }) { + const today = new Date(); + const isToday = today.toISOString().split('T')[0]; + const dateString = todayDate(msg.createdAt); + const formattedTime = formattingTime(msg.createdAt); + + return ( +
+
+ + {isToday === dateString + ? `${formattedTime}` + : `${dateString} ${formattedTime}`} + +
{msg.text}
+
+
+ ); +} diff --git a/components/Chat/OtherMessage.tsx b/components/Chat/OtherMessage.tsx new file mode 100644 index 00000000..d797bcab --- /dev/null +++ b/components/Chat/OtherMessage.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useState } from 'react'; +import { formattingTime, todayDate } from '@/utils/formattedTimeData'; +import { Message } from '@/@types/types'; +import Image from 'next/image'; +import chatAPI from '@/apis/chatAPI'; +import styles from './Chat.module.scss'; + +export default function OtherMessage({ + msg, + prevUserId, +}: { + msg: Message; + prevUserId: string | null; +}) { + const today = new Date(); + const isToday = today.toISOString().split('T')[0]; + const dateString = todayDate(msg.createdAt); + const formattedTime = formattingTime(msg.createdAt); + + const [userName, setUserName] = useState(''); + const [userPicture, setUserPicture] = useState(''); + + const isSameUser = msg.userId === prevUserId; + + useEffect(() => { + const getUserName = async () => { + try { + const response = await chatAPI.getUserInfo(msg.userId); + setUserName(response.data.user.name); + setUserPicture(response.data.user.picture); + } catch (error) { + console.error('Failed to fetch user name:', error); + } + }; + + getUserName(); + }, [msg.id]); + + return ( +
+
+ {!isSameUser && userPicture && ( +
+ 유저사진 + + {userName} +
+ )} +
+
{msg.text}
+ + {isToday === dateString + ? `${formattedTime}` + : `${dateString} ${formattedTime}`} + +
+
+
+ ); +} diff --git a/components/Chat/index.ts b/components/Chat/index.ts new file mode 100644 index 00000000..2f072009 --- /dev/null +++ b/components/Chat/index.ts @@ -0,0 +1,17 @@ +import ChatLoading from './ChatLoading/ChatLoading'; +import ChatAlert from './ChatAlert'; +import ChatroomHeader from './ChatroomHeader'; +import EntryNotice from './EntryNotice'; +import ExitNotice from './ExitNotice'; +import MyMessage from './MyMessage'; +import OtherMessage from './OtherMessage'; + +export { + ChatLoading, + ChatAlert, + ChatroomHeader, + EntryNotice, + ExitNotice, + MyMessage, + OtherMessage, +}; diff --git a/components/ChatList/AllChatListItem/AllChatListItem.module.scss b/components/ChatList/AllChatListItem/AllChatListItem.module.scss new file mode 100644 index 00000000..6add589e --- /dev/null +++ b/components/ChatList/AllChatListItem/AllChatListItem.module.scss @@ -0,0 +1,62 @@ +@import '@/styles/index.scss'; +@import '@/styles/_variables'; + +.itemContainer { + margin: 0; + border-radius: 40rem; + :hover { + background-color: #ececec; + } +} + +.user-profile { + margin-right: 1rem; + border-radius: 100%; +} + +.chat-info { + width: 100%; + display: flex; + justify-content: space-between; +} + +.chat-wrap { + display: flex; + flex-direction: column; +} + +.chat-name-wrap { + display: flex; + .user-length { + color: $gray; + margin-left: 1.7rem; + } +} + +.chat-name { + font-size: $font-size-lg; + margin-bottom: 1rem; + font-weight: bolder; +} + +.chat-lastest-mesaage { + font-size: $font-size-base; +} + +.time-now { + @include time-diff($brand-pink); +} + +.time-minutes { + @include time-diff($success); +} + +.time-hours { + @include time-diff($blue); +} +.time-hours-after { + @include time-diff($gray); +} +.time-days { + @include time-diff($gray); +} diff --git a/components/ChatList/AllChatListItem/AllChatListItem.tsx b/components/ChatList/AllChatListItem/AllChatListItem.tsx new file mode 100644 index 00000000..4d6785b1 --- /dev/null +++ b/components/ChatList/AllChatListItem/AllChatListItem.tsx @@ -0,0 +1,72 @@ +import Image from 'next/image'; +import { Chat } from '@/@types/types'; +import chatListAPI from '@/apis/chatListAPI'; +import { useRouter } from 'next/router'; +import formatTime from '@/utils/timeFormat'; +import styles from './AllChatListItem.module.scss'; +import JudgeWrapper from './JudgeWrapper/JudgeWrapper'; + +interface Props { + chat: Chat; + userId: string; +} + +export default function AllChatListItem({ chat, userId }: Props) { + const checkIncluded = (element: { id: string }) => { + if (element.id === userId) { + return true; + } + return false; + }; + const isincluded = chat.users.some(checkIncluded); + + const router = useRouter(); + const participateChat = async (e: React.MouseEvent) => { + if (e.target instanceof HTMLButtonElement) { + await chatListAPI.participateChat(e.target.id); + router.push(`/chat/${e.target.id}`); + } + }; + + const { timeDiffText, className } = formatTime(chat.updatedAt); + + return ( +
  • + + {`${chat.users[0].username}의 +
    +
    +
    +
    {chat.name}
    + {chat.users.length} +
    + {timeDiffText} +
    +
    +
    + {chat.latestMessage?.text} +
    +
    +
    + {!isincluded && ( + + )} +
    +
    +
    +
  • + ); +} diff --git a/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.module.scss b/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.module.scss new file mode 100644 index 00000000..0cfa3aa3 --- /dev/null +++ b/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.module.scss @@ -0,0 +1,17 @@ +@import '@/styles/index.scss'; +@import '@/styles/_variables'; + +.container { + @include container; + display: flex; + padding: 1rem; + padding: 1.7rem; + border-radius: 2rem; + + button { + @include fill-btn; + margin: 0; + padding: 1rem 1.5rem; + font-size: 1rem; + } +} diff --git a/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.tsx b/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.tsx new file mode 100644 index 00000000..21c43ba5 --- /dev/null +++ b/components/ChatList/AllChatListItem/JudgeWrapper/JudgeWrapper.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link'; +import styles from './JudgeWrapper.module.scss'; + +interface Props { + isincluded: boolean; + chatId: string; + children: React.ReactNode; +} + +export default function JudgeWrapper({ isincluded, chatId, children }: Props) { + if (isincluded) { + return ( + + {children} + + ); + } + return
    {children}
    ; +} diff --git a/components/ChatList/ChatList.module.scss b/components/ChatList/ChatList.module.scss new file mode 100644 index 00000000..ceb32afd --- /dev/null +++ b/components/ChatList/ChatList.module.scss @@ -0,0 +1,100 @@ +@import '@/styles/index.scss'; + +.list_container { + @include container; + margin-top: 8rem; + margin-bottom: 5.6rem; +} + +.titleContainer { + @include container; + @include flex-center-row; + justify-content: flex-start; + color : $brand-pink; + font-size: 1.7rem; + margin: 2rem auto; + p { + + font-weight: bold; + padding : 1rem; + margin: 0; + } + +} + +.itemContainer { + margin: 0; + border-radius: 40rem; + :hover { + background-color: #ececec; + + } + +} + +.container { + @include container; + display: flex; + width: 100%; + padding: 1.7rem; + border-radius: 2rem; + + button { + @include fill-btn; + margin: 0; + padding: 1rem 1.5rem; + font-size: 1rem; + } +} + +.user_profile { + margin-right: 1rem; + border-radius: 100%; +} + +.chatInfo { + width: 100%; + display: flex; + justify-content: space-between; +} + +.chatWrap { + display: flex; + flex-direction: column; +} + +.chatNameWrap { + display: flex; + .user-length { + color: $gray; + margin-left: 1.7rem; + } +} + +.chatName { + font-size: $font-size-lg; + margin-bottom: 1rem; + font-weight: bolder; +} + +.chatLastestMesaage { + font-size: $font-size-base; +} + +.time-now { + @include time-diff($brand-pink); +} + +.time-minutes { + @include time-diff($success); +} + +.time-hours { + @include time-diff($blue); +} +.time-hours-after { + @include time-diff(#c3adde); +} +.time-days { + @include time-diff($gray); +} diff --git a/components/ChatList/ChatListModal/ChatListModal.module.scss b/components/ChatList/ChatListModal/ChatListModal.module.scss new file mode 100644 index 00000000..e7c0775a --- /dev/null +++ b/components/ChatList/ChatListModal/ChatListModal.module.scss @@ -0,0 +1,49 @@ +@import '@/styles/mixin'; +@import '@/styles/_variables'; + +.dim { + @include dim; +} + +.ChatListModalBox { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + width: 30rem; + height: 150px; + border-radius: 20px; + font-size: 1.1rem; + @include flex-center-row; + @include shadow-bottom; + + .close-icon { + font-size: 1.4rem; + position: absolute; + right: 1rem; + top: 1rem; + font-weight: bold; + } + p { + font-size: 1.5rem; + font-weight: bold; + } + + input { + border: 1px solid $brand-pink; + outline: none; + padding: 2rem; + border-radius: 5px; + margin-right: 1.5rem; + } + button { + border: 0; + background-color: $light-pink; + padding: 1.1rem 1.3rem; + border-radius: 7px; + &:hover { + background-color: $brand-pink; + transition: 0.3s; + } + } +} diff --git a/components/ChatList/ChatListModal/ChatListModal.tsx b/components/ChatList/ChatListModal/ChatListModal.tsx new file mode 100644 index 00000000..805daad4 --- /dev/null +++ b/components/ChatList/ChatListModal/ChatListModal.tsx @@ -0,0 +1,56 @@ +import React, { useRef, useState } from 'react'; +import useOnClickOutside from '@/hooks/useOnClickOustside'; +import { BsXCircle } from 'react-icons/bs'; +import { useRouter } from 'next/router'; +import chatListAPI from '@/apis/chatListAPI'; +import { Modal } from '@/components/Common'; +import style from './ChatListModal.module.scss'; + +interface ChatListModalProps { + handleModal: () => void; +} + +export default function ChatListModal({ handleModal }: ChatListModalProps) { + const router = useRouter(); + + const ref = useRef(null); + useOnClickOutside(ref, () => { + handleModal(); + }); + + const [newChatName, setNewChatName] = useState(''); + + const createChat = (e: React.FormEvent) => { + e.preventDefault(); + if (newChatName.length > 15) { + alert('15글자 이내로 이름을 작성해주세요!'); + setNewChatName(''); + return; + } + chatListAPI.createChat({ name: newChatName, users: [] }).then(res => { + router.push(`/chat/${res.data.id}`); + handleModal(); + }); + }; + + return ( +
    + +
    + +

    채팅방 이름

    +
    + setNewChatName(e.target.value)} + placeholder="15글자 이내에 작성해주세요." + /> + +
    +
    +
    +
    + ); +} diff --git a/components/ChatList/CreateChatButton/CreateChatButton.module.scss b/components/ChatList/CreateChatButton/CreateChatButton.module.scss new file mode 100644 index 00000000..365e979e --- /dev/null +++ b/components/ChatList/CreateChatButton/CreateChatButton.module.scss @@ -0,0 +1,20 @@ +@import '@/styles/index.scss'; +@import '@/styles/_variables'; + +.chatPlusBtn { + position: fixed; + width: 50px; + height: 50px; + bottom: 12rem; + right: 3rem; + background-color: $light-pink; + color: white; + border: 0; + box-shadow: lightgray 0rem 0rem 1rem 0.3rem; + font-size: 3rem; + border-radius: 50%; + &:hover { + background-color: $brand-pink; + transition: 0.3s; + } +} diff --git a/components/ChatList/CreateChatButton/CreateChatButton.tsx b/components/ChatList/CreateChatButton/CreateChatButton.tsx new file mode 100644 index 00000000..22d4f51d --- /dev/null +++ b/components/ChatList/CreateChatButton/CreateChatButton.tsx @@ -0,0 +1,17 @@ +import styles from './CreateChatButton.module.scss'; + +interface Props { + setIsModal: React.Dispatch>; +} + +export default function CreateChatButton({ setIsModal }: Props) { + return ( + + ); +} diff --git a/components/ChatList/MyChatListItem.tsx b/components/ChatList/MyChatListItem.tsx new file mode 100644 index 00000000..6052406d --- /dev/null +++ b/components/ChatList/MyChatListItem.tsx @@ -0,0 +1,45 @@ +import { Chat } from '@/@types/types'; +import { formattingTime, todayDate } from '@/utils/formattedTimeData'; +import Image from 'next/image'; +import Link from 'next/link'; +import React from 'react'; +import styles from './ChatList.module.scss'; + +interface Props { + chat: Chat; +} + +export default function MyChatListItem({ chat }: Props) { + const today = new Date(); + const isToday = today.toISOString().split('T')[0]; + + const dateString = todayDate(chat.updatedAt); + const formattedTime = formattingTime(chat.updatedAt); + return ( +
  • + + {`${chat.users[0].username}의 +
    +
    +
    +
    {chat.name}
    + {chat.users.length} +
    +
    + {chat.latestMessage?.text} +
    +
    +
    + {isToday === dateString ? formattedTime : `${dateString}`} +
    +
    + +
  • + ); +} diff --git a/components/ChatList/index.ts b/components/ChatList/index.ts new file mode 100644 index 00000000..3d7c4440 --- /dev/null +++ b/components/ChatList/index.ts @@ -0,0 +1,5 @@ +import ChatListModal from './ChatListModal/ChatListModal'; +import CreateChatButton from './CreateChatButton/CreateChatButton'; +import AllChatListItem from './AllChatListItem/AllChatListItem'; + +export { ChatListModal, CreateChatButton, AllChatListItem }; diff --git a/components/Common/Header/Header.module.scss b/components/Common/Header/Header.module.scss new file mode 100644 index 00000000..b1d202b0 --- /dev/null +++ b/components/Common/Header/Header.module.scss @@ -0,0 +1,33 @@ +@import '@/styles/index.scss'; + +.headerWrapContainer { + width: 100%; + background-color: white; + z-index: 10; + top: 0; + background-color: white; + position: fixed; + display: flex; + justify-content: center; + +} + +.container { + @include container; + width: 100%; + margin: 0 4rem; + padding : 2rem 2rem 0 2rem; + display: flex; + span { + font-size: 1.5rem; + font-weight: bold; + } +} + +.pageName { + margin-left: 1rem; + align-self: center; + font-size: 1.5rem; + } + + diff --git a/components/Common/Header/Header.tsx b/components/Common/Header/Header.tsx new file mode 100644 index 00000000..309ba4a9 --- /dev/null +++ b/components/Common/Header/Header.tsx @@ -0,0 +1,44 @@ +import React, { useState, useEffect } from 'react'; +import Image from 'next/image'; +import style from './Header.module.scss'; + +interface Props { + pageName: string; +} + +export default function Header({ pageName }: Props) { + const [shrink, setShrink] = useState(false); + + const handleScroll = () => { + if (window.scrollY > 50) { + setShrink(true); + } else { + setShrink(false); + } + }; + + useEffect(() => { + window.addEventListener('scroll', handleScroll); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, []); + + return ( +
    +
    +
    + talkhaja_logo +
    + {pageName} +
    +
    + ); +} diff --git a/components/Common/Modal/Modal.module.scss b/components/Common/Modal/Modal.module.scss new file mode 100644 index 00000000..fe017b88 --- /dev/null +++ b/components/Common/Modal/Modal.module.scss @@ -0,0 +1,9 @@ +.ModalBox { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #f8f9fa; + z-index: 20; + border-radius: 1rem; +} diff --git a/components/Common/Modal/Modal.tsx b/components/Common/Modal/Modal.tsx new file mode 100644 index 00000000..d3415f7f --- /dev/null +++ b/components/Common/Modal/Modal.tsx @@ -0,0 +1,9 @@ +import styles from './Modal.module.scss'; + +interface ModalProps { + children: React.ReactNode; +} + +export default function Modal({ children }: ModalProps) { + return
    {children}
    ; +} diff --git a/components/Common/Navigation/Navigation.module.scss b/components/Common/Navigation/Navigation.module.scss new file mode 100644 index 00000000..cabd0ddb --- /dev/null +++ b/components/Common/Navigation/Navigation.module.scss @@ -0,0 +1,52 @@ +@import '@/styles/index.scss'; +.nav { + width: 100%; + background: #fff; + position: fixed; + left: 50%; + transform: translate(-50%); + bottom: 0; + @include shadow-top; + background: #fff; + z-index: 10; + .inner { + max-width: 76.8rem; + width: 100%; + margin: 0 auto; + @include flex-center-row; + justify-content: space-between; + .menu { + width: 25%; + padding: 1rem 0; + text-align: center; + @include flex-center-col; + p { + margin: 0 auto; + padding: 0; + font-size: $font-size-s; + padding-top: 0.4rem; + } + .icon, + p { + font-size: $font-size-s; + color: $black; + transition: color 0.3s; + } + .icon { + font-size: $font-size-lg; + } + :hover { + .icon, + p { + color: $brand-pink; + } + } + } + } +} + +.menu { + a { + width: 100%; + } +} diff --git a/components/Common/Navigation/Navigation.tsx b/components/Common/Navigation/Navigation.tsx new file mode 100644 index 00000000..00cbe533 --- /dev/null +++ b/components/Common/Navigation/Navigation.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import Link from 'next/link'; +import { + BsHouses, + BsPersonCircle, + BsChatSquareHeart, + BsChatSquareQuote, +} from 'react-icons/bs'; +import { useRecoilValue } from 'recoil'; +import showNavigationState from '@/stores/atoms/nav.atoms'; +import styles from './Navigation.module.scss'; + +function Navigation() { + const showNavigation = useRecoilValue(showNavigationState); + + return ( +
    + {showNavigation && ( + + )} +
    + ); +} + +export default Navigation; diff --git a/components/Common/index.ts b/components/Common/index.ts new file mode 100644 index 00000000..0195edca --- /dev/null +++ b/components/Common/index.ts @@ -0,0 +1,5 @@ +import Header from './Header/Header'; +import Modal from './Modal/Modal'; +import Navigation from './Navigation/Navigation'; + +export { Header, Modal, Navigation }; diff --git a/components/HostList/HostDetailsModal/HostDetailsModal.module.scss b/components/HostList/HostDetailsModal/HostDetailsModal.module.scss new file mode 100644 index 00000000..5a37a79c --- /dev/null +++ b/components/HostList/HostDetailsModal/HostDetailsModal.module.scss @@ -0,0 +1,61 @@ +@import '@/styles/index.scss'; +.dim { + @include dim; + z-index: 15; +} + +#ModalBox { + width: 40rem; + display: flex; + flex-direction: column; + padding: 2rem; + box-sizing: border-box; + @include shadow-bottom; + z-index: 20; + height: 100%; + font-size: $font-size-base; + + .close-icon { + font-size: 2.4rem; + position: absolute; + right: 1rem; + font-weight: bold; + } + + .flex-row { + @include flex-center-row; + gap: 2rem; + button, + p { + margin: 0; + } + } + .img-box { + height: 20rem; + overflow-y: hidden; + background-position: center; + background-size: cover; + margin-top: 3rem; + } + + .title { + font-size: $font-size-lg; + } + .text { + margin-top: 0; + line-height: 1.8rem; + b { + display: inline-block; + margin-bottom: 0.4rem; + } + } +} +@media screen and (min-width: 260px) and (max-width: 500px) { + #ModalBox { + z-index: 20; + display: block; + width: 90vw; + overflow-y: scroll; + border-radius: 0; + } +} diff --git a/components/HostList/HostDetailsModal/HostDetailsModal.tsx b/components/HostList/HostDetailsModal/HostDetailsModal.tsx new file mode 100644 index 00000000..2e1d6161 --- /dev/null +++ b/components/HostList/HostDetailsModal/HostDetailsModal.tsx @@ -0,0 +1,98 @@ +import { useRef } from 'react'; +import { BsXCircle } from 'react-icons/bs'; +import Button from '@/components/HostList/HostListItem/Button/Button'; +import useOnClickOutside from '@/hooks/useOnClickOustside'; +import chatListAPI from '@/apis/chatListAPI'; +import { useRouter } from 'next/router'; +import { Chat } from '@/@types/types'; +import { Host } from '@/components/HostList/HostList.types'; +import styles from './HostDetailsModal.module.scss'; +import { Modal } from '../../Common'; + +interface HostDetailsModalProps { + onClose: () => void; + hostDetails: Host; +} + +export default function HostDetailsModal({ + onClose, + hostDetails, +}: HostDetailsModalProps) { + const ref = useRef(null); + useOnClickOutside(ref, () => { + onClose(); + }); + + const router = useRouter(); + const createHostChat = async () => { + // 내가 참여 중인 채팅 목록 + const chatMyList = await chatListAPI.getMyChatList(); + // 숙소와의 채팅만 필터링 + const hostChatList = chatMyList.data.chats.filter( + (chat: Chat) => chat.isPrivate, + ); + + let chatId = ''; + // 숙소와의 채팅 존재 여부 + const isExist = hostChatList.some((chat: Chat) => { + if (chat.users.some(user => user.id === hostDetails.id)) { + chatId = chat.id; + return true; + } + return false; + }); + + // 숙소와 채팅방이 존재하지 않으면 채팅방 생성 + if (!isExist) { + chatListAPI + .createChat({ + name: hostDetails.name, + users: [hostDetails.id], + isPrivate: true, + }) + .then(res => { + router.push(`/chat/${res.data.id}`); + }); + } else { + // 숙소와 채팅방이 존재하면 채팅방으로 이동 + router.push(`/chat/${chatId}`); + } + }; + + return ( + <> +
    + +
    + +
    +
    +

    {hostDetails.name}

    +
    +

    + 주소 : {hostDetails.location} {hostDetails.address} +

    + +

    + 숙소 소개 +
    + {hostDetails.description} +

    +

    + 시설 및 서비스
    {hostDetails.service} +

    +
    + + + ); +} diff --git a/components/HostList/HostList.module.scss b/components/HostList/HostList.module.scss new file mode 100644 index 00000000..d7ede2fb --- /dev/null +++ b/components/HostList/HostList.module.scss @@ -0,0 +1,93 @@ +@import '@/styles/index.scss'; +.container { + @include container; + height: 100%; + padding: 2rem; + padding-bottom: 10rem; + margin-top: 5rem; + + .title { + color: $black; + color: $brand-pink; + font-size: $font-size-lg; + border-bottom: 1px solid $light-pink; + padding: 1rem; + margin-bottom: 2rem; + text-overflow: ellipsis; + white-space: nowrap; + } + + .header { + position: sticky; + background: #fff; + width: 100%; + top: 0; + padding: 1rem 0; + transition: top 0.5s ease-in-out; + z-index: 10; + + &.scrollHeader { + @include shadow-bottom; + } + .inner { + max-width: $max-width; + } + .hash { + display: flex; + justify-content: flex-start; + align-items: center; + flex-wrap: wrap; + gap: 2rem 1rem; + margin: 2rem 0 1rem; + li { + display: inline-block; + button { + cursor: pointer; + padding: 0.6rem 1.2rem; + background: rgba(1, 82, 204, 0.1); + border-radius: 18px; + color: #0152cc; + font-size: small; + font-weight: 600; + border: none; + } + } + } + } + .location { + display: inline-block; + color: $black; + margin: 3rem 0; + &::before { + content: '# '; + } + } + + .itemList { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + li { + text-align: center; + max-width: 100%; + overflow: hidden; + transition: transform 0.3s; + } + :hover { + scale: 1.02; + font-size: $font-size-base; + } + } + + @media screen and (min-width: 430px) and (max-width: 768px) { + .itemList { + grid-template-columns: repeat(3, 1fr); + } + } + + @media screen and (min-width: 768px) { + .itemList { + grid-template-columns: repeat(4, 1fr); + } + } +} diff --git a/components/HostList/HostList.tsx b/components/HostList/HostList.tsx new file mode 100644 index 00000000..29273b27 --- /dev/null +++ b/components/HostList/HostList.tsx @@ -0,0 +1,249 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { + getHostsByLocation, + locations, + getFirebaseData, + fetchAllUsers, +} from '@/utils/hostsStorage'; +import styles from '@/components/HostList/HostList.module.scss'; +import Search from './Search/Search'; +import HostListItem from './HostListItem/HostListItem'; +import HostDetailsModal from './HostDetailsModal/HostDetailsModal'; +import { FirebaseData, Host } from './HostList.types'; + +export default function HostList() { + const [hostData, setHostData] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedHostDetails, setSelectedHostDetails] = useState( + null, + ); + const [filteredHosts, setFilteredHosts] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + const [locationsToShow, setLocationsToShow] = useState([]); + + const [noResultsMessage, setNoResultsMessage] = useState(false); + const [isScrolling, setIsScrolling] = useState(false); + const [showAllButton, setShowAllButton] = useState(true); + + useEffect(() => { + const fetchUserData = async () => { + try { + const [userDataResponse, hostDataResponse] = await Promise.all([ + fetchAllUsers(), + getFirebaseData(), + ]); + + // 데이터 합치기 + const combinedData: Host[] = hostDataResponse.map( + (host: FirebaseData) => { + const hostUserData = userDataResponse.find( + user => user.id === host.id, + ); + return { + ...host, + name: hostUserData ? hostUserData.name : '', + picture: hostUserData ? hostUserData.picture : '', + }; + }, + ); + setHostData(combinedData); + } catch (error) { + console.error('데이터 불러오기 오류:', error); + } + }; + fetchUserData(); + }, []); + + useEffect(() => { + const fetchLocationsToShow = async () => { + try { + const locationPromises = locations.map(getHostsByLocation); + const locationData = await Promise.all(locationPromises); + const uniqueLocations = Array.from( + new Set(locationData.flat().map(host => host.location)), + ); + setLocationsToShow(uniqueLocations); + } catch (error) { + console.error('호스트 위치 데이터 가져오기 오류:', error); + // 에러 처리 또는 사용자에게 알림을 표시하는 등의 추가 작업 수행 + } + }; + fetchLocationsToShow(); + }, []); + + useEffect(() => { + function handleScroll() { + const { scrollY } = window; + setIsScrolling(scrollY > 0); + } + + window.addEventListener('scroll', handleScroll); + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, []); + const headerClass = `${styles.header} ${ + isScrolling ? styles.scrollHeader : '' + }`; + const handleSearch = (query: string) => { + setSearchQuery(query); + // 검색 쿼리가 변경되지 않았다면 캐시된 결과를 사용 + if (query === '') { + setFilteredHosts(hostData); + setLocationsToShow([...locations]); + setShowAllButton(true); + return; + } + // 검색 필터링 + const newFiltered = hostData.filter(host => + Object.values(host).some(value => { + if (typeof value === 'string') { + // 호스트 데이터 속성 값이 문자열인 경우 + return value.toLowerCase().includes(query.toLowerCase()); + } + if (Array.isArray(value)) { + // 호스트 데이터 속성 값이 배열인 경우 + return value.some( + item => + typeof item === 'string' && + item.toLowerCase().includes(query.toLowerCase()), + ); + } + // 다른 데이터 유형에 대한 처리 추가 + return false; + }), + ); + + setFilteredHosts(newFiltered); + + const newLocationsToShow = Array.from( + new Set(newFiltered.map(host => host.location)), + ); + setLocationsToShow(newLocationsToShow); + + if (newFiltered.length === 0) { + setNoResultsMessage(true); + setShowAllButton(false); + + setTimeout(() => { + setNoResultsMessage(false); + setSearchQuery(''); + setFilteredHosts(hostData); + setLocationsToShow([...locations]); + setShowAllButton(true); + }, 1000); + } else { + setShowAllButton(true); + } + }; + + const handleClearSearch = useCallback(() => { + setFilteredHosts(hostData); + setLocationsToShow([...locations]); + setSearchQuery(''); + }, [hostData]); + + useEffect(() => { + if (!searchQuery) { + handleClearSearch(); + } + }, [searchQuery, handleClearSearch]); + + const handleOpenModal = (host: Host) => { + setSelectedHostDetails(host); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setSelectedHostDetails(null); + setIsModalOpen(false); + }; + + const scrollToLocation = (location: string) => { + const element = document.getElementById(location); + if (element) { + const yOffset = element.getBoundingClientRect().top - 100; + window.scrollBy({ top: yOffset, behavior: 'smooth' }); + } + }; + const handleShowAll = () => { + handleClearSearch(); + scrollToLocation('host-list'); + }; + const displayHosts = searchQuery ? filteredHosts : hostData; + + return ( + <> +
    +

    HOT PLACE ✨ 인기 지역 숙소 모음

    +
    +
    + +
      + {showAllButton && ( +
    • + +
    • + )} + {locationsToShow.map(location => ( +
    • + +
    • + ))} +
    +
    +
    +
    + {locationsToShow.map(location => ( +
    + {(!searchQuery || + displayHosts.some(host => host.location === location)) && ( +

    {location} 숙소

    + )} +
      + {searchQuery + ? displayHosts + .filter(host => host.location === location) + .map(host => ( + handleOpenModal(host)} + /> + )) + : hostData + .filter(host => host.location === location) + .map(host => ( + handleOpenModal(host)} + /> + ))} +
    +
    + ))} +
    + + {noResultsMessage &&

    검색 결과가 없습니다.

    } +
    + {isModalOpen && ( + + )} + + ); +} diff --git a/components/HostList/HostList.types.ts b/components/HostList/HostList.types.ts new file mode 100644 index 00000000..375181d6 --- /dev/null +++ b/components/HostList/HostList.types.ts @@ -0,0 +1,14 @@ +export interface FirebaseData { + id: string; + location: string; + address: string; + description: string; + service: string; +} +export interface ApiData { + id: string; + name: string; + picture: string; +} + +export interface Host extends FirebaseData, ApiData {} diff --git a/components/HostList/HostListItem/Button/Button.module.scss b/components/HostList/HostListItem/Button/Button.module.scss new file mode 100644 index 00000000..3980e34e --- /dev/null +++ b/components/HostList/HostListItem/Button/Button.module.scss @@ -0,0 +1,9 @@ +@import '@/styles/index.scss'; + +.fill-btn { + @include fill-btn; +} + +.line-btn { + @include line-btn; +} diff --git a/components/HostList/HostListItem/Button/Button.tsx b/components/HostList/HostListItem/Button/Button.tsx new file mode 100644 index 00000000..53550cd6 --- /dev/null +++ b/components/HostList/HostListItem/Button/Button.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import styles from './Button.module.scss'; + +interface ButtonProps { + onClick: () => void; + text: string; + className: string; +} +export default function Button({ onClick, text, className = '' }: ButtonProps) { + return ( + + ); +} diff --git a/components/HostList/HostListItem/HostListItem.module.scss b/components/HostList/HostListItem/HostListItem.module.scss new file mode 100644 index 00000000..18be13ea --- /dev/null +++ b/components/HostList/HostListItem/HostListItem.module.scss @@ -0,0 +1,17 @@ +@import '@/styles/index.scss'; + +.item { + @include flex-center-col; + justify-content: space-between; + gap: 1rem; + border: 1px solid #e6e6e6; + border-radius: 10px; + padding: 2rem; + + .host-img { + border-radius: 50px; + } + .name { + font-weight: bold; + } +} diff --git a/components/HostList/HostListItem/HostListItem.tsx b/components/HostList/HostListItem/HostListItem.tsx new file mode 100644 index 00000000..89b7dbd7 --- /dev/null +++ b/components/HostList/HostListItem/HostListItem.tsx @@ -0,0 +1,30 @@ +import Image from 'next/image'; +import { Host } from '@/components/HostList/HostList.types'; +import styles from './HostListItem.module.scss'; +import Button from './Button/Button'; + +interface HostListItemProps { + host: Host; + openModal: () => void; +} +export default function HostListItem({ host, openModal }: HostListItemProps) { + if (host) { + return ( +
  • + {host.name} +
    +

    {host.name}

    +
    +
  • + ); + } + + return null; +} diff --git a/components/HostList/Search/Search.module.scss b/components/HostList/Search/Search.module.scss new file mode 100644 index 00000000..f1316592 --- /dev/null +++ b/components/HostList/Search/Search.module.scss @@ -0,0 +1,30 @@ +@import '@/styles/index.scss'; + +.search { + width: 100%; + @include flex-center-row; + align-items: center; + gap: 0.2rem; + box-sizing: border-box; + input { + width: 80%; + border-radius: 5px; + border: 2px solid $light-blue; + transition: border-color 0.3s ease; + outline: none; + padding: 1rem; + box-sizing: border-box; + &:focus { + border-color: $light-pink; + } + } + .fill-btn { + width: 20%; + text-align: center; + @include fill-btn; + font-size: 1.6rem; + font-weight: bold; + margin: 0; + height: 4.1rem; + } +} diff --git a/components/HostList/Search/Search.tsx b/components/HostList/Search/Search.tsx new file mode 100644 index 00000000..2235d01a --- /dev/null +++ b/components/HostList/Search/Search.tsx @@ -0,0 +1,46 @@ +import styles from './Search.module.scss'; + +interface SearchProps { + onSearch: (query: string) => void; + searchQuery: string | null; + setSearchQuery: (query: string | null) => void; +} + +export default function Search({ + onSearch, + searchQuery, + setSearchQuery, +}: SearchProps) { + function handleSearch() { + if (searchQuery !== null) { + onSearch(searchQuery); + } + } + + function handleKeyDown(e: React.KeyboardEvent) { + if (e.key === 'Enter' && searchQuery !== null) { + onSearch(searchQuery); + } + } + + return ( +
    + setSearchQuery(e.target.value)} + onKeyDown={handleKeyDown} + /> + + +
    + ); +} diff --git a/components/MyPage.module.scss b/components/MyPage.module.scss new file mode 100644 index 00000000..94a6598c --- /dev/null +++ b/components/MyPage.module.scss @@ -0,0 +1,110 @@ +@import '@/styles/mixin'; +@import '@/styles/variables'; + +.myPageWrapper { + @include flex-center-row; + height: 100vh; + button { + border: none; + color: #ffff; + font-size: 1.5rem; + cursor: pointer; + border-radius: 0.5rem; + height: 4rem; + background-color: $light-pink; + &:hover { + background-color: $brand-pink; + transition: 0.3s; + } + } + + .myPageBox { + width: 40rem; + @include flex-center-col; + gap: 1.5rem; + margin-bottom: 10rem; + + .myPageTitle { + font-size: 4rem; + } + + h2 { + font-size: 3rem; + margin-top: 0; + margin-bottom: 2rem; + } + + hr { + width: 100%; + } + + .myPageForm { + width: 100%; + border-radius: 5px; + border: 2px solid $light-gray; + padding: 1rem; + box-sizing: border-box; + + .idNameBox { + display: flex; + align-items: center; + height: 3rem; + width: 100%; + font-size: 2rem; + margin: 1rem 0; + + input { + outline: none; + padding: 0.2rem 1rem; + font-size: 2rem; + width: 50%; + border: 1px solid #ced4da; + border-radius: 0.5rem; + } + + .idName { + display: flex; + justify-content: center; + font-weight: bold; + width: 30%; + } + + button { + width: 15%; + height: 3.3rem; + margin-left: 1rem; + } + } + + .userImgContainer { + display: flex; + flex-direction: column; + align-items: center; + gap: 3rem; + margin-bottom: 2rem; + + .imageButtonContainer { + .updateImageButton { + background-color: $light-pink; + &:hover { + background-color: $brand-pink; + transition: 0.3s; + } + } + button { + margin: 0 1rem; + background-color: #ced4da; + } + .imageInputButton { + display: none; + } + } + } + } + + .logoutButton { + width: 100%; + height: 5rem; + } + } +} diff --git a/components/Signup/SignUpModal/SignUpModal.module.scss b/components/Signup/SignUpModal/SignUpModal.module.scss new file mode 100644 index 00000000..2702c7f2 --- /dev/null +++ b/components/Signup/SignUpModal/SignUpModal.module.scss @@ -0,0 +1,71 @@ +@import '@/styles/mixin'; + +.dim { + @include dim; +} + +.signUpModalBox { + display: flex; + flex-direction: column; + align-items: center; + gap: 3rem; + width: 50rem; + + h2 { + font-size: 2rem; + margin-top: 4rem; + } + + .userImgBox { + position: relative; + border: 1px solid #dee2e6; + border-radius: 50%; + width: 15rem; + height: 15rem; + @include flex-center-row; + + .camaraIcon { + position: absolute; + bottom: 0; + font-size: 3rem; + right: -1rem; + color: #000; + cursor: pointer; + } + + .resetIcon { + position: absolute; + bottom: 0; + font-size: 3rem; + left: -1rem; + color: #000; + cursor: pointer; + } + } + + .actionButtonsWrapper { + display: flex; + justify-content: center; + gap: 3rem; + margin-bottom: 2rem; + + button { + cursor: pointer; + padding: 0.5rem 1rem; + border: none; + width: 10rem; + height: 3.5rem; + font-size: 1.4rem; + border-radius: 0.5rem; + background-color: #ff5991; + color: #ffff; + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + } + } + input { + display: none; + } +} diff --git a/components/Signup/SignUpModal/SignUpModal.tsx b/components/Signup/SignUpModal/SignUpModal.tsx new file mode 100644 index 00000000..beb2ab8c --- /dev/null +++ b/components/Signup/SignUpModal/SignUpModal.tsx @@ -0,0 +1,173 @@ +import instance from '@/apis/axios'; +import app from '@/utils/firebaseConfig'; +import { + getDownloadURL, + getStorage, + ref, + uploadString, +} from 'firebase/storage'; +import Image from 'next/image'; +import { useRouter } from 'next/router'; +import { useRef, useState } from 'react'; +import { BsCameraFill, BsFillTrash3Fill } from 'react-icons/bs'; +import styles from './SignUpModal.module.scss'; +import Loading from '../SignupLoading/SignupLoading'; +import { Modal } from '../../Common'; + +interface RequestBody { + id: string; + password: string; + name: string; +} + +interface UserRequestBody extends RequestBody { + picture: string; +} + +interface SignUpModalProps { + handleModal: () => void; + formData: RequestBody; +} + +export default function SignUpModal({ + handleModal, + formData, +}: SignUpModalProps) { + const router = useRouter(); + const userFormData: RequestBody = formData; + const [pictureName, setPictureName] = useState('default.jpg'); + const DEFAULT_IMAGE_URL = + 'https://firebasestorage.googleapis.com/v0/b/talk-eaae8.appspot.com/o/images%2Fdefault.jpg?alt=media&token=6ca482c2-bcb0-48cc-b673-76ad2b4ce943'; + const [selectedImg, setSelectedImg] = useState(DEFAULT_IMAGE_URL); + const imageRef = useRef(null); + const imageStyle = { + borderRadius: '50%', + }; + const [isLoading, setIsLoading] = useState(false); + + const handleInputClick = () => { + if (imageRef.current) { + imageRef.current.click(); + } + }; + + const handleImgResetClick = () => { + setSelectedImg(DEFAULT_IMAGE_URL); + setPictureName('default.jpg'); + }; + + const previewImg = (img: File | undefined) => { + if (img) { + setPictureName(img?.name); + + const fileReader = new FileReader(); + fileReader.onload = event => { + if (event.target) { + setSelectedImg(event.target.result as string); + } + }; + fileReader.readAsDataURL(img); + } + }; + + const handleFileChange = (e: React.ChangeEvent) => { + const selectedFile: File | undefined = e.target.files?.[0]; + previewImg(selectedFile); + }; + + const handleImageUpload = async (): Promise => { + try { + if (selectedImg) { + const storage = getStorage(app); + const imagesRef = ref(storage, `images/${pictureName}`); + + await uploadString(imagesRef, selectedImg, 'data_url'); + const url = await getDownloadURL(imagesRef); + return url; + } + } catch (error) { + console.log(error); + } + return undefined; + }; + + const handleSignUpClick = async (e: React.MouseEvent) => { + e.preventDefault(); + setIsLoading(true); // 로딩 시작 + console.log(isLoading); + try { + const selectedImgUrl: string | undefined = await handleImageUpload(); + if (selectedImgUrl) { + const requestUserData: UserRequestBody = { + ...userFormData, + picture: selectedImgUrl, + }; + await instance.post('/signup', requestUserData); + } else { + const requestUserData: UserRequestBody = { + ...userFormData, + picture: DEFAULT_IMAGE_URL, + }; + await instance.post('/signup', requestUserData); + } + router.push('/login'); + } catch (error) { + console.log(error); + // 에러 처리 알림 + } finally { + setIsLoading(false); // 로딩 종료 + } + }; + + return ( +
    + {isLoading ? ( + + ) : ( + +
    +

    프로필 사진 설정

    +
    + {selectedImg ? ( + 이미지 미리보기 + ) : ( + '' + )} + + + +
    +
    + +
    +
    + + +
    +
    +
    + )} +
    + ); +} diff --git a/components/Signup/SignupLoading/SignupLoading.module.scss b/components/Signup/SignupLoading/SignupLoading.module.scss new file mode 100644 index 00000000..438506bc --- /dev/null +++ b/components/Signup/SignupLoading/SignupLoading.module.scss @@ -0,0 +1,17 @@ +@import '@/styles/mixin'; + +.container { + @include flex-center-col; + position: absolute; + top: 50%; + left: 50%; + z-index: 1; + transform: translate(-50%, -50%); + background-color: white; + padding: 5rem 7rem; + border-radius: 2rem; + + h3 { + font-size: 1.2rem; + } +} diff --git a/components/Signup/SignupLoading/SignupLoading.tsx b/components/Signup/SignupLoading/SignupLoading.tsx new file mode 100644 index 00000000..68f21073 --- /dev/null +++ b/components/Signup/SignupLoading/SignupLoading.tsx @@ -0,0 +1,17 @@ +import Image from 'next/image'; +import React from 'react'; +import styles from './SignupLoading.module.scss'; + +export default function SignupLoading() { + return ( +
    +

    회원가입 중입니다.

    + loading +
    + ); +} diff --git a/components/Signup/index.ts b/components/Signup/index.ts new file mode 100644 index 00000000..714e4b07 --- /dev/null +++ b/components/Signup/index.ts @@ -0,0 +1,4 @@ +import SignupLoading from './SignupLoading/SignupLoading'; +import SignUpModal from './SignUpModal/SignUpModal'; + +export { SignupLoading, SignUpModal }; diff --git a/components/login.module.scss b/components/login.module.scss new file mode 100644 index 00000000..d891f795 --- /dev/null +++ b/components/login.module.scss @@ -0,0 +1,86 @@ +@import '@/styles/mixin'; + +.login_container { + @include flex-center-row; + height: 100vh; + + .login_box { + padding: 0 1rem; + @include flex-center-col; + h2 { + font-size: 3rem; + margin-top: 0; + margin-bottom: 4rem; + } + } + + .input_box { + display: flex; + flex-direction: column; + gap: 0.7rem; + margin-bottom: 2rem; + + input { + width: 40rem; + height: 4rem; + padding: 0.2rem 1rem; + outline: none; + border: 1px solid #dee2e6; + border-radius: 5px; + font-size: 1.4rem; + + &::placeholder { + font-size: 1.4rem; + } + } + } + + .button_box { + display: flex; + flex-direction: column; + gap: 1.5rem; + align-items: center; + + .horizontalLine { + width: 100%; + border: 1px solid #d1cfcf; + } + + button { + width: 100%; + background-color: #ff5991; + border: none; + color: #ffff; + cursor: pointer; + height: 4rem; + border-radius: 5px; + font-size: 1.4rem; + + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + } + + .signUp_link { + width: 100%; + height: 4rem; + background-color: #ff5991; + @include flex-center-row; + border-radius: 5px; + cursor: pointer; + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + + a { + @include flex-center-row; + width: 100%; + color: #ffff; + font-size: 1.4rem; + text-decoration: none; + } + } + } +} diff --git a/components/signUp.module.scss b/components/signUp.module.scss new file mode 100644 index 00000000..15b11c83 --- /dev/null +++ b/components/signUp.module.scss @@ -0,0 +1,130 @@ +@import '@/styles/mixin'; + +.signUp_container { + @include flex-center-row; + height: 100vh; + + .signUp_box { + @include flex-center-col; + padding: 0 1rem; + margin-bottom: 10rem; + + h2 { + font-size: 3rem; + margin-bottom: 2.5rem; + } + } + + .input_box { + display: flex; + flex-direction: column; + gap: 0.7rem; + + .idBox { + display: flex; + justify-content: space-between; + + input { + width: 27rem; + } + + button { + background-color: #ff5991; + border: none; + border-radius: 5px; + color: #ffff; + cursor: pointer; + + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + } + } + + input { + height: 4rem; + padding: 0.2rem 0.5rem; + outline: none; + font-size: 1.4rem; + border: 1px solid #dee2e6; + border-radius: 5px; + &:focus { + border: 2px solid #ff5991; + } + + &::placeholder { + font-size: 1.4rem; + } + } + + label { + font-size: 1.5rem; + font-weight: 600; + } + + span { + font-size: 1.1rem; + font-weight: 700; + padding: 0 0.4rem; + color: #919191; + } + } + + .terms_agree { + display: flex; + gap: 0.7rem; + margin: 1rem 0; + + input { + cursor: pointer; + width: 2rem; + } + + p { + font-size: 1.4rem; + font-weight: 700; + } + } + + .button_box { + display: flex; + flex-direction: column; + gap: 1rem; + + .back_link { + width: 100%; + height: 4rem; + background-color: #ff5991; + @include flex-center-row; + border-radius: 5px; + cursor: pointer; + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + + a { + @include flex-center-row; + width: 100%; + color: #ffff; + font-size: 1.4rem; + text-decoration: none; + } + } + + button { + background-color: #ff5991; + border: none; + color: #ffff; + cursor: pointer; + height: 4rem; + border-radius: 5px; + font-size: 1.4rem; + &:hover { + background-color: #ff3478; + transition: 0.3s; + } + } + } +} diff --git a/constants/userLoinState.ts b/constants/userLoinState.ts new file mode 100644 index 00000000..f5732787 --- /dev/null +++ b/constants/userLoinState.ts @@ -0,0 +1,8 @@ +const SIGNOUT_USER_STATE = { + id: null, + name: null, + picture: null, + isLoggedIn: false, +}; + +export default SIGNOUT_USER_STATE; diff --git a/hooks/useConnectServerSocket.tsx b/hooks/useConnectServerSocket.tsx new file mode 100644 index 00000000..ce0c7f9c --- /dev/null +++ b/hooks/useConnectServerSocket.tsx @@ -0,0 +1,36 @@ +import { getCookie } from 'cookies-next'; +import { useEffect, useMemo } from 'react'; +import { io } from 'socket.io-client'; + +export default function useConnectServerSocket() { + const accessToken = getCookie('ACCESS_TOKEN'); + + const serverSocket = useMemo(() => { + return io(`${process.env.NEXT_PUBLIC_SERVER_URL}`, { + extraHeaders: { + Authorization: `Bearer ${accessToken}`, + serverId: process.env.NEXT_PUBLIC_API_KEY!, + }, + }); + }, [accessToken]); + + useEffect(() => { + serverSocket.on('connect', () => { + console.log('Connected from server socket'); + }); + serverSocket.on('error', error => { + console.error('server socket 연결 중 error:', error); + }); + serverSocket.on('disconnect', () => { + console.log('Disconnected from server socket'); + }); + + return () => { + serverSocket.off('connect'); + serverSocket.off('error'); + serverSocket.disconnect(); + }; + }, [serverSocket]); + + return serverSocket; +} diff --git a/hooks/useOnClickOustside.tsx b/hooks/useOnClickOustside.tsx new file mode 100644 index 00000000..79c6156f --- /dev/null +++ b/hooks/useOnClickOustside.tsx @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; + +export default function useOnClickOutside( + ref: React.RefObject, + handler: () => void, +) { + useEffect(() => { + const listener = (event: MouseEvent | TouchEvent) => { + if (!ref.current || ref.current.contains(event.target as Node)) { + return; + } + handler(); + }; + + document.addEventListener('mousedown', listener); + document.addEventListener('touchstart', listener); + return () => { + document.removeEventListener('mousedown', listener); + document.removeEventListener('touchstart', listener); + }; + }, [ref, handler]); +} diff --git a/next.config.js b/next.config.js new file mode 100644 index 00000000..bf55f3a2 --- /dev/null +++ b/next.config.js @@ -0,0 +1,10 @@ +/** @type {import('next').NextConfig} */ + +const nextConfig = { + images: { + domains: ['firebasestorage.googleapis.com', 'gravatar.com'], + }, + reactStrictMode: false, +}; + +module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..679fc27f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6294 @@ +{ + "name": "talkhaja", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "talkhaja", + "version": "0.1.0", + "dependencies": { + "axios": "^1.6.0", + "classnames": "^2.3.2", + "cookies-next": "^4.0.0", + "firebase": "^10.6.0", + "jwt-decode": "^4.0.0", + "next": "14.0.1", + "next-cookies": "^2.0.3", + "react": "^18", + "react-cookie": "^6.1.1", + "react-dom": "^18", + "react-hook-form": "^7.48.2", + "react-icons": "^4.11.0", + "recoil": "^0.7.7", + "recoil-persist": "^5.1.0", + "socket.io": "^4.7.2", + "socket.io-client": "^4.7.2" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@types/socket.io": "^3.0.2", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "eslint": "^8.53.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-typescript": "^17.1.0", + "eslint-config-next": "14.0.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "prettier": "^3.0.3", + "sass": "^1.69.5", + "typescript": "^5", + "typescript-plugin-css-modules": "^5.0.2" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" + }, + "node_modules/@firebase/app": { + "version": "0.9.23", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.23.tgz", + "integrity": "sha512-CA5pQ88We3FhyuesGKn1thaPBsJSGJGm6AlFToOmEJagWqBeDoNJqBkry/BsHnCs9xeYWWIprKxvuFmAFkdqoA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.0.tgz", + "integrity": "sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz", + "integrity": "sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==", + "dependencies": { + "@firebase/app-check": "0.8.0", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.23.tgz", + "integrity": "sha512-UCv0LEzcoqAgY+sLsau7aOZz0CJNLN2gESY68bHKmukNXEN6onLPxBKJzn68CsZZGcdiIEXwvrum1riWNPe9Gw==", + "dependencies": { + "@firebase/app": "0.9.23", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" + }, + "node_modules/@firebase/auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.4.0.tgz", + "integrity": "sha512-SfFXZCHDbY+7oSR52NSwx0U7LjYiA+N8imloxphCf3/F+MFty/+mhdjSXGtrJYd0Gbud/qcyedfn2XnWJeIB/g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.9.tgz", + "integrity": "sha512-Fw03i7vduIciEBG4imLtA1duJbljgkfbxiBo/EuekcB+BnPxHp+e8OGMUfemPYeO7Munj6kUC9gr5DelsQkiNA==", + "dependencies": { + "@firebase/auth": "1.4.0", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", + "dependencies": { + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.1.tgz", + "integrity": "sha512-VAhF7gYwunW4Lw/+RQZvW8dlsf2r0YYqV9W0Gi2Mz8+0TGg1mBJWoUtsHfOr8kPJXhcLsC4eP/z3x6L/Fvjk/A==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.1.tgz", + "integrity": "sha512-ky82yLIboLxtAIWyW/52a6HLMVTzD2kpZlEilVDok73pNPLjkJYowj8iaIWK5nTy7+6Gxt7d00zfjL6zckGdXQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/database": "1.0.1", + "@firebase/database-types": "1.0.0", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", + "dependencies": { + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.3.2.tgz", + "integrity": "sha512-K4TwMbgArWw+XAEUYX/vtk+TVy9n1uLeJKSrQeb89lwfkfyFINGLPME6YleaS0ovD1ziLM5/0WgL1CR4s53fDg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.22.tgz", + "integrity": "sha512-M166UvFvRri0CK/+5N0MIeXJVxR6BsX0/96xFT506DxRPIFezLjLcvfddtyFgfe0CtyQWoxBXt060uWUg3d/sw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "4.3.2", + "@firebase/firestore-types": "3.0.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.0.tgz", + "integrity": "sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.10.0.tgz", + "integrity": "sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.5.tgz", + "integrity": "sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.10.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, + "node_modules/@firebase/logger": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.4.tgz", + "integrity": "sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz", + "integrity": "sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "node_modules/@firebase/messaging/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" + }, + "node_modules/@firebase/storage": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.11.2.tgz", + "integrity": "sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.2.tgz", + "integrity": "sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.3.tgz", + "integrity": "sha512-+ZplYUN3HOpgCfgInqgdDAbkGGVzES1cs32JJpeqoh87SkRobGXElJx+1GZSaDqzFL+bYiX18qEcBK76mYs8uA==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.9.tgz", + "integrity": "sha512-vQ1qwi/Kiyprt+uhb1+rHMpyk4CVRMTGNUGGPRGS7pLNfWkdCHrGEnT6T3/JyC2VZgoOX/X1KwdoU0WYQAeYcQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@next/env": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.1.tgz", + "integrity": "sha512-Ms8ZswqY65/YfcjrlcIwMPD7Rg/dVjdLapMcSHG26W6O67EJDF435ShW4H4LXi1xKO1oRc97tLXUpx8jpLe86A==" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.0.1.tgz", + "integrity": "sha512-bLjJMwXdzvhnQOnxvHoTTUh/+PYk6FF/DCgHi4BXwXCINer+o1ZYfL9aVeezj/oI7wqGJOqwGIXrlBvPbAId3w==", + "dev": true, + "dependencies": { + "glob": "7.1.7" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.1.tgz", + "integrity": "sha512-JyxnGCS4qT67hdOKQ0CkgFTp+PXub5W1wsGvIq98TNbF3YEIN7iDekYhYsZzc8Ov0pWEsghQt+tANdidITCLaw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.1.tgz", + "integrity": "sha512-625Z7bb5AyIzswF9hvfZWa+HTwFZw+Jn3lOBNZB87lUS0iuCYDHqk3ujuHCkiyPtSC0xFBtYDLcrZ11mF/ap3w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.1.tgz", + "integrity": "sha512-iVpn3KG3DprFXzVHM09kvb//4CNNXBQ9NB/pTm8LO+vnnnaObnzFdS5KM+w1okwa32xH0g8EvZIhoB3fI3mS1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.1.tgz", + "integrity": "sha512-mVsGyMxTLWZXyD5sen6kGOTYVOO67lZjLApIj/JsTEEohDDt1im2nkspzfV5MvhfS7diDw6Rp/xvAQaWZTv1Ww==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.1.tgz", + "integrity": "sha512-wMqf90uDWN001NqCM/auRl3+qVVeKfjJdT9XW+RMIOf+rhUzadmYJu++tp2y+hUbb6GTRhT+VjQzcgg/QTD9NQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.1.tgz", + "integrity": "sha512-ol1X1e24w4j4QwdeNjfX0f+Nza25n+ymY0T2frTyalVczUmzkVD7QGgPTZMHfR1aLrO69hBs0G3QBYaj22J5GQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.1.tgz", + "integrity": "sha512-WEmTEeWs6yRUEnUlahTgvZteh5RJc4sEjCQIodJlZZ5/VJwVP8p2L7l6VhzQhT4h7KvLx/Ed4UViBdne6zpIsw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.1.tgz", + "integrity": "sha512-oFpHphN4ygAgZUKjzga7SoH2VGbEJXZa/KL8bHCAwCjDWle6R1SpiGOdUdA8EJ9YsG1TYWpzY6FTbUA+iAJeww==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.1.tgz", + "integrity": "sha512-FFp3nOJ/5qSpeWT0BZQ+YE1pSMk4IMpkME/1DwKBwhg4mJLB9L+6EXuJi4JEwaJdl5iN+UUlmUD3IsR1kx5fAg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz", + "integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==", + "dev": true + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.16.tgz", + "integrity": "sha512-Trx5or1Nyg1Fq138PCuWqoApzvoSLWzZ25ORBiHMbbUT42g578lH1GT4TwYDbiUOLFuDsCkfLneT2105fsFWGg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/postcss-modules-local-by-default": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.2.tgz", + "integrity": "sha512-CtYCcD+L+trB3reJPny+bKWKMzPfxEyQpKIwit7kErnOexf5/faaGpkFy4I5AwbV4hp1sk7/aTg0tt0B67VkLQ==", + "dev": true, + "dependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@types/postcss-modules-scope": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.4.tgz", + "integrity": "sha512-//ygSisVq9kVI0sqx3UPLzWIMCmtSVrzdljtuaAEJtGoGnpjBikZ2sXO5MpH9SnWX9HRfXxHifDAXcQjupWnIQ==", + "dev": true, + "dependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.10", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" + }, + "node_modules/@types/react": { + "version": "18.2.36", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.36.tgz", + "integrity": "sha512-o9XFsHYLLZ4+sb9CWUYwHqFVoG61SesydF353vFMMsQziiyRu8np4n2OYMUSDZ8XuImxDr9c5tR7gidlH29Vnw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", + "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" + }, + "node_modules/@types/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "dev": true + }, + "node_modules/@types/socket.io": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz", + "integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==", + "deprecated": "This is a stub types definition. socket.io provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "socket.io": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "devOptional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "devOptional": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookies-next": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-4.0.0.tgz", + "integrity": "sha512-3TyzeltFCGgdOlVOVTPClSq+YV9ZCdOyA3aHRZv9f5aSgg7EyI4NSvXFOCgzT/xIxeHR4Rz8/z5Tdo9oPqaVpA==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/node": "^16.10.2", + "cookie": "^0.4.0" + } + }, + "node_modules/cookies-next/node_modules/@types/node": { + "version": "16.18.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.61.tgz", + "integrity": "sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/engine.io": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.3.tgz", + "integrity": "sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz", + "integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", + "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.13.0 || ^6.0.0", + "@typescript-eslint/parser": "^5.0.0 || ^6.0.0", + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3" + } + }, + "node_modules/eslint-config-next": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.0.1.tgz", + "integrity": "sha512-QfIFK2WD39H4WOespjgf6PLv9Bpsd7KGGelCtmq4l67nGvnlsGpuvj0hIT+aIy6p5gKH+lAChYILsyDlxP52yg==", + "dev": true, + "dependencies": { + "@next/eslint-plugin-next": "14.0.1", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "devOptional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/firebase": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.6.0.tgz", + "integrity": "sha512-bnYwHwZ6zB+dM6mGQPEXcFHtAT2WoVzG6H4SIR8HzURVGKJxBW+TqfP3qcJQjTZV3tDqDTo/XZkVmoU/SovV8A==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.23", + "@firebase/app-check": "0.8.0", + "@firebase/app-check-compat": "0.3.7", + "@firebase/app-compat": "0.2.23", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "1.4.0", + "@firebase/auth-compat": "0.4.9", + "@firebase/database": "1.0.1", + "@firebase/database-compat": "1.0.1", + "@firebase/firestore": "4.3.2", + "@firebase/firestore-compat": "0.3.22", + "@firebase/functions": "0.10.0", + "@firebase/functions-compat": "0.3.5", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.4", + "@firebase/messaging-compat": "0.2.4", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-compat": "0.3.2", + "@firebase/util": "1.9.3" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "devOptional": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/next/-/next-14.0.1.tgz", + "integrity": "sha512-s4YaLpE4b0gmb3ggtmpmV+wt+lPRuGtANzojMQ2+gmBpgX9w5fTbjsy6dXByBuENsdCX5pukZH/GxdFgO62+pA==", + "dependencies": { + "@next/env": "14.0.1", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.31", + "styled-jsx": "5.1.1", + "watchpack": "2.4.0" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.15.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.0.1", + "@next/swc-darwin-x64": "14.0.1", + "@next/swc-linux-arm64-gnu": "14.0.1", + "@next/swc-linux-arm64-musl": "14.0.1", + "@next/swc-linux-x64-gnu": "14.0.1", + "@next/swc-linux-x64-musl": "14.0.1", + "@next/swc-win32-arm64-msvc": "14.0.1", + "@next/swc-win32-ia32-msvc": "14.0.1", + "@next/swc-win32-x64-msvc": "14.0.1" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-cookies": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/next-cookies/-/next-cookies-2.0.3.tgz", + "integrity": "sha512-YVCQzwZx+sz+KqLO4y9niHH9jjz6jajlEQbAKfsYVT6DOfngb/0k5l6vFK4rmpExVug96pGag8OBsdSRL9FZhQ==", + "dependencies": { + "universal-cookie": "^4.0.2" + } + }, + "node_modules/next-cookies/node_modules/@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, + "node_modules/next-cookies/node_modules/universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "dependencies": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-6.1.1.tgz", + "integrity": "sha512-fuFRpf8LH6SfmVMowDUIRywJF5jAUDUWrm0EI5VdXfTl5bPcJ7B0zWbuYpT0Tvikx7Gs18MlvAT+P+744dUz2g==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^6.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.48.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz", + "integrity": "sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/react-icons": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", + "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "devOptional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recoil": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", + "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/recoil-persist": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/recoil-persist/-/recoil-persist-5.1.0.tgz", + "integrity": "sha512-sew4k3uBVJjRWKCSFuBw07Y1p1pBOb0UxLJPxn4G2bX/9xNj+r2xlqYy/BRfyofR/ANfqBU04MIvulppU4ZC0w==", + "peerDependencies": { + "recoil": "^0.7.2" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reserved-words": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", + "integrity": "sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "optional": true + }, + "node_modules/sass": { + "version": "1.69.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", + "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true, + "optional": true + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/stylus/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-plugin-css-modules": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.0.2.tgz", + "integrity": "sha512-ej/Og4Y8mF+43P14P9Ik1MGqNXcXBVgO1TltkESegdnZsaaRXnaJ5CoJmTPRkg25ysQlOV6P94wNhI4VxIzlkw==", + "dev": true, + "dependencies": { + "@types/postcss-modules-local-by-default": "^4.0.0", + "@types/postcss-modules-scope": "^3.0.1", + "dotenv": "^16.0.3", + "icss-utils": "^5.1.0", + "less": "^4.1.3", + "lodash.camelcase": "^4.3.0", + "postcss": "^8.4.21", + "postcss-load-config": "^3.1.4", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "reserved-words": "^0.1.2", + "sass": "^1.58.3", + "source-map-js": "^1.0.2", + "stylus": "^0.59.0", + "tsconfig-paths": "^4.1.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/typescript-plugin-css-modules/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript-plugin-css-modules/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universal-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-6.1.1.tgz", + "integrity": "sha512-33S9x3CpdUnnjwTNs2Fgc41WGve2tdLtvaK2kPSbZRc5pGpz2vQFbRWMxlATsxNNe/Cy8SzmnmbuBM85jpZPtA==", + "dependencies": { + "@types/cookie": "^0.5.1", + "cookie": "^0.5.0" + } + }, + "node_modules/universal-cookie/node_modules/@types/cookie": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.4.tgz", + "integrity": "sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==" + }, + "node_modules/universal-cookie/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..b21847dc --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "talkhaja", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "axios": "^1.6.0", + "cookies-next": "^4.0.0", + "classnames": "^2.3.2", + "firebase": "^10.6.0", + "jwt-decode": "^4.0.0", + "next": "14.0.1", + "next-cookies": "^2.0.3", + "react": "^18", + "react-cookie": "^6.1.1", + "react-dom": "^18", + "react-hook-form": "^7.48.2", + "react-icons": "^4.11.0", + "recoil": "^0.7.7", + "recoil-persist": "^5.1.0", + "socket.io": "^4.7.2", + "socket.io-client": "^4.7.2" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@types/socket.io": "^3.0.2", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "eslint": "^8.53.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-typescript": "^17.1.0", + "eslint-config-next": "14.0.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "prettier": "^3.0.3", + "sass": "^1.69.5", + "typescript": "^5", + "typescript-plugin-css-modules": "^5.0.2" + } +} diff --git a/pages/_app.tsx b/pages/_app.tsx new file mode 100644 index 00000000..b4529d23 --- /dev/null +++ b/pages/_app.tsx @@ -0,0 +1,87 @@ +import { RecoilRoot, RecoilEnv, MutableSnapshot } from 'recoil'; +import type { AppProps, AppContext } from 'next/app'; +import '@/styles/normalize.scss'; +import '@/styles/fonts.scss'; +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; +import cookies from 'next-cookies'; +import App from 'next/app'; +import { removeTokenAll } from '@/utils/tokenManager'; +import { Navigation } from '@/components/Common'; +import authorizeFetch from '@/utils/authorizeFetch'; +import userTokenState from '@/stores/atoms/userTokenState'; +import SIGNOUT_USER_STATE from '@/constants/userLoinState'; + +function MyApp({ + Component, + pageProps, + userData, // eslint-disable-next-line @typescript-eslint/no-explicit-any +}: AppProps | any) { + RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false; + const { pathname } = useRouter(); + const initialState = ({ set }: MutableSnapshot) => { + const { id, name, picture } = userData; + const isLoggedIn = id !== null; + set(userTokenState, { id, name, picture, isLoggedIn }); + }; + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return ( + + + + + ); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +MyApp.getInitialProps = async (appContext: AppContext): Promise => { + const appProps = await App.getInitialProps(appContext); + const { ctx } = appContext; + const allCookies = cookies(ctx); + + const accessToken = allCookies.ACCESS_TOKEN; + const refreshToken = allCookies.REFRESH_TOKEN; + + const removeAllCookies = () => { + // 서버 사이드 쿠키 삭제 + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + ctx.res && + ctx.res.setHeader('Set-Cookie', [ + `ACCESS_TOKEN=deleted; Max-Age=0`, + `REFRESH_TOKEN=deleted; Max-Age=0`, + ]); + // 클라이언트 사이드 쿠키 삭제 + removeTokenAll(); + }; + + let userState = SIGNOUT_USER_STATE; + if (refreshToken && accessToken) { + try { + const { isAuth, data } = await authorizeFetch({ + accessToken, + refreshToken, + }); + + userState = isAuth ? { ...data, isLoggedIn: true } : SIGNOUT_USER_STATE; + + if (!isAuth) { + removeAllCookies(); + } + } catch (e) { + removeAllCookies(); + userState = SIGNOUT_USER_STATE; + } + } + + if (userState.id === null) { + removeAllCookies(); + } + + return { ...appProps, userData: userState }; +}; + +export default MyApp; diff --git a/pages/_document.tsx b/pages/_document.tsx new file mode 100644 index 00000000..ad9eb5ec --- /dev/null +++ b/pages/_document.tsx @@ -0,0 +1,14 @@ +import { Html, Head, Main, NextScript } from 'next/document'; + +export default function Document() { + return ( + + + +
    + +
    + ); +} + +export const getServerSideProps = async ( + context: GetServerSidePropsContext, +) => { + const refreshToken = context.req.cookies.REFRESH_TOKEN; + + if (refreshToken) { + return { + redirect: { + destination: '/', + permanent: false, + }, + }; + } + return { + props: {}, + }; +}; diff --git a/pages/mypage.tsx b/pages/mypage.tsx new file mode 100644 index 00000000..0a673c6e --- /dev/null +++ b/pages/mypage.tsx @@ -0,0 +1,248 @@ +import app from '@/utils/firebaseConfig'; +import { removeTokenAll } from '@/utils/tokenManager'; +import Image from 'next/image'; +import { useRef, useState } from 'react'; +import { useSetRecoilState } from 'recoil'; +import Jwtinterceptor from '@/apis/JwtInterceptor'; +import SIGNOUT_USER_STATE from '@/constants/userLoinState'; +import userTokenState from '@/stores/atoms/userTokenState'; +import authorizeFetch from '@/utils/authorizeFetch'; +import { + getDownloadURL, + getStorage, + ref, + uploadString, +} from 'firebase/storage'; +import { GetServerSidePropsContext } from 'next'; +import { useRouter } from 'next/router'; +import styles from '@/components/MyPage.module.scss'; + +interface PatchResponseValue { + messgae: string; +} + +const { instance } = Jwtinterceptor(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default function MyPage({ userData }: any) { + const router = useRouter(); + const imageRef = useRef(null); + const imageStyle = { + borderRadius: '50%', + }; + const DEFAULT_IMAGE_URL = + 'https://firebasestorage.googleapis.com/v0/b/talk-eaae8.appspot.com/o/images%2Fdefault.jpg?alt=media&token=6ca482c2-bcb0-48cc-b673-76ad2b4ce943'; + const { id, name, picture } = userData.user; + const [pictureName, setPictureName] = useState('default.jpg'); + const [selectedImg, setSelectedImg] = useState(picture); + const [userName, setUserName] = useState(name); + const setUser = useSetRecoilState(userTokenState); + const handleInputClick = () => { + if (imageRef.current) { + imageRef.current.click(); + } + }; + + const previewImg = (img: File | undefined) => { + if (img) { + setPictureName(img?.name); + + const fileReader = new FileReader(); + fileReader.onload = event => { + if (event.target) { + setSelectedImg(event.target.result as string); + } + }; + fileReader.readAsDataURL(img); + } + }; + + const handleFileChange = (e: React.ChangeEvent) => { + const selectedFile: File | undefined = e.target.files?.[0]; + previewImg(selectedFile); + }; + + const imageUpload = async (): Promise => { + try { + if (selectedImg) { + const storage = getStorage(app); + const imagesRef = ref(storage, `images/${pictureName}`); + + await uploadString(imagesRef, selectedImg, 'data_url'); + const url = await getDownloadURL(imagesRef); + return url; + } + } catch (error) { + console.log(error); + } + return undefined; + }; + + const handleImageSaveClick = async ( + e: React.MouseEvent, + ) => { + e.preventDefault(); + try { + const selectedImgUrl: string | undefined = await imageUpload(); + if (selectedImgUrl) { + await instance.patch('/user', { + picture: selectedImgUrl, + }); + alert('사진이 변경되었습니다.'); + setSelectedImg(selectedImgUrl); + } + } catch (error) { + console.log(error); + } + }; + + const handleImageDeleteClick = async ( + e: React.MouseEvent, + ) => { + e.preventDefault(); + try { + if (picture !== DEFAULT_IMAGE_URL) { + await instance.patch('/user', { + picture: DEFAULT_IMAGE_URL, + }); + alert('기본이미지로 변경되었습니다.'); + setSelectedImg(DEFAULT_IMAGE_URL); + } + } catch (error) { + console.log(error); + } + }; + + const handlePatchUserNameClick = async ( + e: React.MouseEvent, + ) => { + e.preventDefault(); + + try { + const requestUserName = userName.trim(); + if (name !== requestUserName) { + await instance.patch('/user', { name: requestUserName }); + alert('이름이 수정되었습니다.'); + } + } catch (error) { + console.log(error); + } + }; + + const handleLogoutClick = (e: React.MouseEvent) => { + e.preventDefault(); + if (window.confirm('로그아웃 하시겠습니까?')) { + setUser(SIGNOUT_USER_STATE); + removeTokenAll(); + router.push('/login'); + } + }; + + return ( +
    +
    + talkhaja_logo +

    마이페이지

    +
    +
    +
    + {picture && selectedImg && ( + 프로필 사진 + )} +
    +
    + + + + +
    +
    +
    +
    +
    아이디
    + {id} +
    +
    +
    이름
    + setUserName(e.target.value)} + /> + +
    +
    +
    + +
    +
    + ); +} + +export const getServerSideProps = async ( + context: GetServerSidePropsContext, +) => { + const accessToken = context.req.cookies.ACCESS_TOKEN; + const refreshToken = context.req.cookies.REFRESH_TOKEN; + + if (accessToken && refreshToken) { + const response = await authorizeFetch({ + accessToken, + refreshToken, + }); + return { + props: { userData: response.data }, + }; + } + if (!refreshToken) { + // accessToken이 없으면 로그인 페이지로 리다이렉트 + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; + } + return { + props: {}, + }; +}; diff --git a/pages/signup.tsx b/pages/signup.tsx new file mode 100644 index 00000000..ab70fbce --- /dev/null +++ b/pages/signup.tsx @@ -0,0 +1,240 @@ +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import Link from 'next/link'; +import { GetServerSidePropsContext } from 'next'; +import { useSetRecoilState } from 'recoil'; +import Image from 'next/image'; +import instance from '@/apis/axios'; +import { SignUpModal } from '@/components/Signup'; +import styles from '@/components/signUp.module.scss'; +import showNavigationState from '@/stores/atoms/nav.atoms'; + +interface RequestBody { + id: string; // 사용자 아이디 (필수!, 영어와 숫자만) + password: string; // 사용자 비밀번호, 5자 이상 (필수!) + name: string; // 사용자 이름, 20자 이하 (필수!) +} + +interface FormTypes extends RequestBody { + passwordConfirm: string; +} + +export default function SignUp() { + const { + register, + handleSubmit, + formState: { errors }, + trigger, + setError, + getValues, + } = useForm({ mode: 'onChange' }); + const [agreeToTerms, setAgreeToTerms] = useState(false); + const [isModal, setIsModal] = useState(false); + const [portalElement, setPotalElement] = useState(null); + const [formData, setFormData] = useState({ + id: '', + password: '', + name: '', + }); + const [checkId, setCheckId] = useState(false); + const [checkIdMessage, setCheckIdMessage] = useState(''); + + const checkDuplicateId = async ( + id: string, + e: React.MouseEvent, + ): Promise => { + e.preventDefault(); + const isValid = await trigger('id'); + if (!isValid) { + return; + } + try { + const response = await instance.post('/check/id', { id }); + if (response.data.isDuplicated) { + setCheckId(false); + setError( + 'id', + { message: '이미 등록된 아이디입니다.' }, + { shouldFocus: true }, + ); + } else { + setCheckId(true); + setCheckIdMessage('사용가능한 아이디입니다.'); + } + } catch (error) { + console.log(error); + } + }; + + useEffect(() => { + setPotalElement(document.getElementById('modal-root')); + }, [isModal]); + + const handleTerms = (e: React.ChangeEvent) => { + setAgreeToTerms(e.target.checked); + }; + + const handleModal = () => { + setIsModal(!isModal); + }; + + const onValid: SubmitHandler = (data, e) => { + e?.preventDefault(); + const { id, password, name, passwordConfirm } = data; + if (agreeToTerms && password === passwordConfirm && checkId) { + setFormData(prev => ({ + ...prev, + id, + password, + name, + })); + handleModal(); + } + }; + + const onInValid = () => { + console.log('유효하지 않는 입력이다.'); + }; + + const setShowNavigation = useSetRecoilState(showNavigationState); + useEffect(() => { + setShowNavigation(false); + return () => { + setShowNavigation(true); + }; + }, []); + + return ( + <> +
    +
    + talkhaja_logo +

    회원가입

    +
    +
    + + + {errors.name?.message} + +
    + { + if (checkId) { + setCheckId(false); + } + }, + })} + id="id" + type="text" + placeholder="영어와 숫자만 입력해주세요" + /> + +
    + {checkId ? ( + {checkIdMessage} + ) : ( + {errors.id?.message} + )} + + + {errors.password?.message} + + + getValues('password') !== value + ? '비밀번호와 일치하지 않습니다.' + : true, + }, + })} + id="passwordConfirm" + type="password" + placeholder="비밀번호를 한번 더 입력해주세요" + /> + {errors.passwordConfirm?.message} +
    +
    + +

    톡하자 이용 약관 및 개인 정보 처리 방침에 동의합니다.

    +
    +
    + +
    + + 뒤로가기 + +
    +
    +
    +
    +
    + {isModal && portalElement + ? createPortal( + , + portalElement, + ) + : null} + + ); +} + +export const getServerSideProps = async ( + context: GetServerSidePropsContext, +) => { + const refreshToken = context.req.cookies.REFRESH_TOKEN; + + if (refreshToken) { + return { + redirect: { + destination: '/', + permanent: false, + }, + }; + } + return { + props: {}, + }; +}; diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/fonts/NotoSansKR-Bold.ttf b/public/fonts/NotoSansKR-Bold.ttf new file mode 100644 index 00000000..6cf639eb Binary files /dev/null and b/public/fonts/NotoSansKR-Bold.ttf differ diff --git a/public/fonts/NotoSansKR-Medium.ttf b/public/fonts/NotoSansKR-Medium.ttf new file mode 100644 index 00000000..5311c8a3 Binary files /dev/null and b/public/fonts/NotoSansKR-Medium.ttf differ diff --git a/public/fonts/NotoSansKR-Regular.ttf b/public/fonts/NotoSansKR-Regular.ttf new file mode 100644 index 00000000..1b14d324 Binary files /dev/null and b/public/fonts/NotoSansKR-Regular.ttf differ diff --git a/public/fonts/fonts.css b/public/fonts/fonts.css new file mode 100644 index 00000000..e9db17fc --- /dev/null +++ b/public/fonts/fonts.css @@ -0,0 +1,20 @@ +@font-face { + font-family: "Yanolja_Bold"; + src: url("./Yanolja_Bold.ttf"); +} +@font-face { + font-family: "Yanolja_Regular"; + src: url("./Yanolja_Regular.ttf"); +} +@font-face { + font-family: "NotoSans-Regular"; + src: url("./NotoSansKR-Regular.ttf"); +} +@font-face { + font-family: "NotoSans-Medium"; + src: url("./NotoSansKR-Medium.ttf"); +} +@font-face { + font-family: "NotoSans-Bold"; + src: url("./NotoSansKR-Bold.ttf"); +}/*# sourceMappingURL=fonts.css.map */ \ No newline at end of file diff --git a/public/images/Spin-1s-200px.gif b/public/images/Spin-1s-200px.gif new file mode 100644 index 00000000..3d5da807 Binary files /dev/null and b/public/images/Spin-1s-200px.gif differ diff --git a/public/images/Talkhaja.svg b/public/images/Talkhaja.svg new file mode 100644 index 00000000..11ef43a9 --- /dev/null +++ b/public/images/Talkhaja.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 00000000..da4e8077 Binary files /dev/null and b/public/images/logo.png differ diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 00000000..d2f84222 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/stores/atoms/nav.atoms.ts b/stores/atoms/nav.atoms.ts new file mode 100644 index 00000000..0ec21c0a --- /dev/null +++ b/stores/atoms/nav.atoms.ts @@ -0,0 +1,8 @@ +import { atom } from 'recoil'; + +const showNavigationState = atom({ + key: 'showNavigationState', + default: true, +}); + +export default showNavigationState; diff --git a/stores/atoms/user.atoms.ts b/stores/atoms/user.atoms.ts new file mode 100644 index 00000000..711ca174 --- /dev/null +++ b/stores/atoms/user.atoms.ts @@ -0,0 +1,12 @@ +import { atom } from 'recoil'; +import { recoilPersist } from 'recoil-persist'; + +const { persistAtom } = recoilPersist(); + +const userIdState = atom({ + key: 'userIdState', + default: null, + effects_UNSTABLE: [persistAtom], +}); + +export default userIdState; diff --git a/stores/atoms/userTokenState.ts b/stores/atoms/userTokenState.ts new file mode 100644 index 00000000..572d791e --- /dev/null +++ b/stores/atoms/userTokenState.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import authorizeFetch from '@/utils/authorizeFetch'; +import { + getAccessToken, + getRefreshToken, + removeTokenAll, +} from '@/utils/tokenManager'; +import { atom } from 'recoil'; + +const SIGNOUT_USER_STATE = { + id: null, + name: null, + picture: null, + isLoggedIn: false, +}; + +const cookieEffect = + (accessTokenkey: 'ACCESS_TOKEN', refreshTokenKey: 'REFRESH_TOKEN') => + ({ setSelf, onSet }: any) => { + onSet(async () => { + try { + const accessToken: string = getAccessToken(); + const refreshToken: string = getRefreshToken(); + + if (!accessToken || !refreshToken) { + removeTokenAll(); + return SIGNOUT_USER_STATE; + } + + const { data } = await authorizeFetch({ + accessToken, + refreshToken, + }); + + const { auth } = data; + const { id, name, picture } = data.user; + return { id, name, picture, auth, isLoggedIn: true }; + } catch (error: unknown) { + removeTokenAll(); + console.log(error); + return SIGNOUT_USER_STATE; + } + }); + }; + +const userTokenState = atom({ + key: `user/${new Date().getUTCMilliseconds() * Math.random()}`, + effects: [cookieEffect('ACCESS_TOKEN', 'REFRESH_TOKEN')], + default: SIGNOUT_USER_STATE, +}); + +export default userTokenState; diff --git a/styles/_mixin.scss b/styles/_mixin.scss new file mode 100644 index 00000000..b481eb0d --- /dev/null +++ b/styles/_mixin.scss @@ -0,0 +1,102 @@ +@import '@/styles/variables.scss'; + +@mixin a11y-hidden() { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + clip-path: polygon(0 0, 0 0, 0 0); +} + +@mixin container() { + max-width: $max-width; + width: 100%; + margin: 0 auto; + font-size: $font-size-base; + box-sizing: border-box; + background-color: white; +} + +@mixin flex-center-row() { + display: flex; + justify-content: center; + align-items: center; +} + +@mixin flex-between-row() { + display: flex; + justify-content: space-between; + align-items: center; +} + +@mixin flex-center-col() { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +@mixin shadow-top() { + box-shadow: 0 0 0.4rem 0 rgba(0, 0, 0, 0.16); +} +@mixin shadow-bottom() { + box-shadow: 0 4px 4px -4px rgba(0, 0, 0, 0.3); +} + +@mixin button() { + border: none; + background: transparent; +} + +@mixin fill-btn() { + border-radius: 5px; + padding: 1rem 2rem; + cursor: pointer; + color: #fff; + background: $light-pink; + border: none; + transition: color 0.3s; + margin: 1rem 0 0; + &:hover { + background: $brand-pink; + } +} +@mixin line-btn() { + color: $light-pink; + background: transparent; + border: 1px solid $light-pink; + transition: color 0.3s; + border-radius: 5px; + padding: 1rem 2rem; + margin: 1rem 0 0; + cursor: pointer; + &:hover { + color: #fff; + background: $brand-pink; + } +} + +@mixin dim() { + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background-color: rgba($color: $black, $alpha: 0.7); +} + +@mixin time-diff($color) { + color: $color; + border: 1px solid $color; + background: rgba($color, 0.05); + padding: 0.2rem 0.6rem; + border-radius: 5rem; + font-size: $font-size-s; + letter-spacing: -0.1rem; + text-wrap: nowrap; + text-align: center; + display: inline-block; + margin-left: 0.8rem; +} diff --git a/styles/_variables.scss b/styles/_variables.scss new file mode 100644 index 00000000..65315bc9 --- /dev/null +++ b/styles/_variables.scss @@ -0,0 +1,18 @@ +$black: #151515; +$brand-pink: #ff3478; +$brand-blue: #002d79; +$light-pink: #ff5991; +$light-gray: #f2f2f2; +$light-blue: rgba(1, 82, 204, 0.1); +$blue: #0152cc; +$gray: #aaa; + +$failed: #e74a3b; +$success: #1cc88a; + +$font-size-logo: 4rem; +$font-size-lg: 1.7rem; +$font-size-base: 1.4rem; +$font-size-s: 1.2rem; + +$max-width: 76.8rem; diff --git a/styles/fonts.scss b/styles/fonts.scss new file mode 100644 index 00000000..2d62bc1f --- /dev/null +++ b/styles/fonts.scss @@ -0,0 +1,14 @@ +@font-face { + font-family: 'NotoSans-Regular'; + src: url('../public/fonts/NotoSansKR-Regular.ttf'); +} + +@font-face { + font-family: 'NotoSans-Medium'; + src: url('../public/fonts/NotoSansKR-Medium.ttf'); +} + +@font-face { + font-family: 'NotoSans-Bold'; + src: url('../public/fonts/NotoSansKR-Bold.ttf'); +} diff --git a/styles/index.scss b/styles/index.scss new file mode 100644 index 00000000..f77ffa8b --- /dev/null +++ b/styles/index.scss @@ -0,0 +1,2 @@ +@import '@/styles/variables'; +@import '@/styles/mixin'; diff --git a/styles/normalize.scss b/styles/normalize.scss new file mode 100644 index 00000000..2062d239 --- /dev/null +++ b/styles/normalize.scss @@ -0,0 +1,364 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + font-size: 62.5%; +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; + font-family: 'NotoSans-Regular'; + height: 100%; + background-color: #f2f2f2; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; + text-decoration: none; + color: #000; + cursor: pointer; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { + /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type='button']:-moz-focusring, +[type='reset']:-moz-focusring, +[type='submit']:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type='checkbox'], +[type='radio'] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..00e83582 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "baseUrl": ".", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { + "@/*": ["./*"] + }, + "plugins": [{ "name": "typescript-plugin-css-modules" }] + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/utils/authorizeFetch.ts b/utils/authorizeFetch.ts new file mode 100644 index 00000000..db75b8bb --- /dev/null +++ b/utils/authorizeFetch.ts @@ -0,0 +1,57 @@ +import axios, { AxiosError } from 'axios'; +import { setToken } from './tokenManager'; + +const instance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_BASE_URL, +}); + +// eslint-disable-next-line consistent-return +export default async function authorizeFetch({ + accessToken, + refreshToken, +}: { + accessToken: string; + refreshToken: string; +}) { + const headers = { + 'Content-Type': 'application/json', + serverId: process.env.NEXT_PUBLIC_API_KEY, + withCredentials: true, + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : null), + }; + + try { + const { data } = await instance.get('/auth/me', { + headers, + }); + return { data, isAuth: true }; + } catch (error) { + if (error instanceof AxiosError) { + if (error.response && error.response.status === 400) { + try { + const { data: newTokenData } = await instance.post('/refresh', { + refreshToken, + }); + + const newAccessToken = newTokenData.data.accessToken; + setToken('ACCESS_TOKEN', newAccessToken); + + const { data } = await instance.post('/auth/me', { + headers: { + 'Content-Type': 'application/json', + serverId: process.env.NEXT_PUBLIC_API_KEY, + withCredentials: true, + Authorizaiton: `Bearer ${newAccessToken}`, + }, + }); + + return { data, isAuth: true }; + } catch (e) { + console.log(e); + return { isAuth: false }; + } + } + } + return { isAuth: false }; + } +} diff --git a/utils/chatList.ts b/utils/chatList.ts new file mode 100644 index 00000000..32576e2c --- /dev/null +++ b/utils/chatList.ts @@ -0,0 +1,13 @@ +import { Chat } from '@/@types/types'; +import { convertToMilliSecond } from './formattedTimeData'; + +export const sortChatList = (chatList: Chat[]) => { + const sortedChatList = chatList.sort( + (a, b) => + convertToMilliSecond(b.updatedAt) - convertToMilliSecond(a.updatedAt), + ); + return sortedChatList; +}; + +export const filterPrivateChat = (chatList: Chat[], isPrivate: boolean) => + chatList.filter((chat: Chat) => chat.isPrivate === isPrivate); diff --git a/utils/firebaseConfig.ts b/utils/firebaseConfig.ts new file mode 100644 index 00000000..40277620 --- /dev/null +++ b/utils/firebaseConfig.ts @@ -0,0 +1,13 @@ +import { initializeApp } from 'firebase/app'; + +const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, +}; +const app = initializeApp(firebaseConfig); +export default app; + diff --git a/utils/formattedTimeData.ts b/utils/formattedTimeData.ts new file mode 100644 index 00000000..08298f0c --- /dev/null +++ b/utils/formattedTimeData.ts @@ -0,0 +1,23 @@ +export const formattingTime = (time: Date) => { + const options: Intl.DateTimeFormatOptions = { + hour: 'numeric', // 'string'이 아닌 'numeric' 리터럴 타입 + minute: 'numeric', // 'string'이 아닌 'numeric' 리터럴 타입 + hour12: true, + }; + const timeString = new Date(time) + .toLocaleTimeString('ko-KR', options) + .replace('오전', '오전 ') + .replace('오후', '오후 '); + + return timeString; +}; + +export const todayDate = (time: Date) => { + const messageDate = new Date(time); + const dateString = messageDate.toISOString().split('T')[0]; + return dateString; +}; + +export const convertToMilliSecond = (date: Date) => { + return new Date(date).getTime(); +}; diff --git a/utils/hostsStorage.ts b/utils/hostsStorage.ts new file mode 100644 index 00000000..c7683780 --- /dev/null +++ b/utils/hostsStorage.ts @@ -0,0 +1,62 @@ +import { getStorage } from 'firebase/storage'; +import { + getFirestore, + collection, + query, + where, + getDocs, +} from 'firebase/firestore'; +import { FirebaseData, ApiData } from '@/components/HostList/HostList.types'; +import userListAPI from '@/apis/userListAPI'; +import app from './firebaseConfig'; + +export const storage = getStorage(app); +export const db = getFirestore(app); +export const hostsCollection = collection(db, 'hosts'); +export const locations = ['부산', '제주', '강릉', '여수', '양양']; + +// 지역별 데이터 필터링 +export const getHostsByLocation = async ( + location: string, +): Promise => { + try { + const locationQuery = query( + hostsCollection, + where('location', '==', location), + ); + const snapshot = await getDocs(locationQuery); + + const hosts: FirebaseData[] = []; + + snapshot.forEach(hostDoc => { + hosts.push(hostDoc.data() as FirebaseData); + }); + return hosts; + } catch (error) { + console.error('getHostsByLocation err', error); // eslint-disable-line no-console + throw error; + } +}; + +// firebase에서 hosts 데이터 가져오기 +export const getFirebaseData = async (): Promise => { + const querySnapshot = await getDocs(hostsCollection); + + const firebaseHostData: FirebaseData[] = []; + querySnapshot.forEach(doc => { + const data = doc.data() as FirebaseData; + firebaseHostData.push(data); + }); + return firebaseHostData; +}; + +// 전체 유저 조회 +export async function fetchAllUsers(): Promise { + try { + const response = await userListAPI.getAllUserList(); + return response.data; + } catch (error) { + console.error('response.data err', error); + throw error; + } +} diff --git a/utils/loginStorage.ts b/utils/loginStorage.ts new file mode 100644 index 00000000..e0dfc872 --- /dev/null +++ b/utils/loginStorage.ts @@ -0,0 +1,28 @@ +const storage = typeof window !== 'undefined' ? localStorage : null; + +export const getStorage = (key: string, defaultValue = undefined) => { + try { + const storedValue = storage?.getItem(key); + + return storedValue || defaultValue; + } catch (error) { + console.error(error); + return defaultValue; + } +}; + +export const setStorage = (key: string, value: string) => { + try { + storage?.setItem(key, value); + } catch (error) { + console.error(error); + } +}; + +export const removeStorage = (key: string) => { + try { + storage?.removeItem(key); + } catch (error) { + console.error(error); + } +}; diff --git a/utils/timeFormat.ts b/utils/timeFormat.ts new file mode 100644 index 00000000..a512df92 --- /dev/null +++ b/utils/timeFormat.ts @@ -0,0 +1,39 @@ +interface TimeFormatResult { + timeDiffText: string; + className: string; +} + +export default function formatTime(updatedAt: Date | string): TimeFormatResult { + const now = new Date(); + const messageTime = new Date(updatedAt); + const timeDiffInSeconds = (now.getTime() - messageTime.getTime()) / 1000; + + if (timeDiffInSeconds < 300) { + // 5분 + return { timeDiffText: '방금', className: 'time-now' }; + } + + if (timeDiffInSeconds < 3600) { + // 1시간 미만 + const minutesAgo = Math.floor(timeDiffInSeconds / 60); + return { timeDiffText: `${minutesAgo} 분 전`, className: 'time-minutes' }; + } + + if (timeDiffInSeconds < 14400) { + // 4시간 미만 + const hoursAgo = Math.floor(timeDiffInSeconds / 3600); + return { timeDiffText: `${hoursAgo} 시간 전`, className: 'time-hours' }; + } + + if (timeDiffInSeconds < 86400) { + // 1일 미만 + const hoursAgo = Math.floor(timeDiffInSeconds / 3600); + return { + timeDiffText: `${hoursAgo} 시간 전`, + className: 'time-hours-after', + }; + } + + const days = Math.floor(timeDiffInSeconds / 86400); + return { timeDiffText: `${days} 일 전`, className: 'time-days' }; +} diff --git a/utils/tokenManager.ts b/utils/tokenManager.ts new file mode 100644 index 00000000..b5f5c0de --- /dev/null +++ b/utils/tokenManager.ts @@ -0,0 +1,30 @@ +import { Cookies } from 'react-cookie'; + +const cookies = new Cookies(); + +export function setToken(key: 'ACCESS_TOKEN' | 'REFRESH_TOKEN', token: string) { + const expires = new Date(); + expires.setDate(expires.getDate() + 7); + + cookies.set(key, token, { + path: '/', + expires: key === 'ACCESS_TOKEN' ? expires : undefined, + }); +} + +export function removeToken(key: 'ACCESS_TOKEN' | 'REFRESH_TOKEN') { + cookies.remove(key, { path: '/' }); +} + +export function removeTokenAll() { + removeToken('ACCESS_TOKEN'); + removeToken('REFRESH_TOKEN'); +} + +export function getAccessToken() { + return cookies.get('ACCESS_TOKEN'); +} + +export function getRefreshToken() { + return cookies.get('REFRESH_TOKEN'); +}