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 @@
-# 🍋 소켓 기반 채팅앱
+
+
+
+
+### 함께 나누어 더 즐거운 여행 수다, Talk하자!
+
+### Talkhaja에서 유저들과 함께 여러분들의 여정을 공유하고 즐기세요.
+
+
+
+
+
+
+
+
+
+
+
+### 🔐 테스트 계정
+>ID: test5
+>PW: 11111
+
+### 🗓️ 개발 기간: `2주(13일) 23.11.06 ~ 23.11.16`
+
+## :clap: Contributors
+
+
+
+## 🛢️ 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 (
+
+
채팅방을 불러오는 중입니다..
+
+
+ );
+}
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 (
+
+ );
+}
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 (
+
+ );
+}
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.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 (
+
+ );
+}
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.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 (
+
+ );
+}
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 (
+
+
+
+
+ );
+ }
+
+ 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 (
+
+
회원가입 중입니다.
+
+
+ );
+}
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 (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/pages/chat-list/all.tsx b/pages/chat-list/all.tsx
new file mode 100644
index 00000000..ed0b5209
--- /dev/null
+++ b/pages/chat-list/all.tsx
@@ -0,0 +1,96 @@
+import { Chat, IsValidAuth } from '@/@types/types';
+import { useEffect, useState } from 'react';
+import { sortChatList } from '@/utils/chatList';
+import useConnectServerSocket from '@/hooks/useConnectServerSocket';
+import { Header } from '@/components/Common';
+import {
+ AllChatListItem,
+ ChatListModal,
+ CreateChatButton,
+} from '@/components/ChatList';
+import { GetServerSidePropsContext } from 'next';
+import authorizeFetch from '@/utils/authorizeFetch';
+import chatListAPI from '@/apis/chatListAPI';
+import styles from '@/components/ChatList/ChatList.module.scss';
+
+export default function AllChatList({ authData }: IsValidAuth) {
+ const [allChatList, setAllChatList] = useState([]);
+
+ const getAllChat = async () => {
+ const allChats: Chat[] = (await chatListAPI.getAllChatList()).data.chats;
+ const sortedAllChatList = sortChatList(allChats);
+ setAllChatList(sortedAllChatList);
+ };
+
+ // 30초마다 채팅방 목록 재요청
+ useEffect(() => {
+ getAllChat();
+ const timer = setInterval(() => {
+ getAllChat();
+ }, 30000);
+
+ return () => clearInterval(timer);
+ }, []);
+
+ const [isModal, setIsModal] = useState(false);
+
+ const handleModal = () => {
+ setIsModal(!isModal);
+ };
+
+ const serverSocket = useConnectServerSocket();
+ useEffect(() => {
+ serverSocket.on('new-chat', ({ responseChat }) => {
+ setAllChatList(preState => [responseChat, ...preState]);
+ });
+ return () => {
+ serverSocket.off('new-chat');
+ };
+ }, [serverSocket]);
+
+ return (
+
+
+
+ {allChatList.map(chat => (
+
+ ))}
+
+
+ {isModal &&
}
+
+ );
+}
+
+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: { authData: response.data },
+ };
+ }
+ if (!refreshToken) {
+ // accessToken이 없으면 로그인 페이지로 리다이렉트
+ return {
+ redirect: {
+ destination: '/login',
+ permanent: false,
+ },
+ };
+ }
+ return {
+ props: {},
+ };
+};
diff --git a/pages/chat-list/my.tsx b/pages/chat-list/my.tsx
new file mode 100644
index 00000000..ada48ed1
--- /dev/null
+++ b/pages/chat-list/my.tsx
@@ -0,0 +1,85 @@
+import { useEffect, useState } from 'react';
+import { IoIosChatbubbles } from 'react-icons/io';
+import { GetServerSidePropsContext } from 'next';
+import { Chat } from '@/@types/types';
+import chatListAPI from '@/apis/chatListAPI';
+import { filterPrivateChat, sortChatList } from '@/utils/chatList';
+import { Header } from '@/components/Common';
+import MyChatListItem from '@/components/ChatList/MyChatListItem';
+import authorizeFetch from '@/utils/authorizeFetch';
+import styles from '@/components/ChatList/ChatList.module.scss';
+
+export default function MyChatList() {
+ const [myHostChatList, setMyHostChatList] = useState([]);
+ const [myChatList, setMyChatList] = useState([]);
+ const getMyChat = async () => {
+ const myChats = (await chatListAPI.getMyChatList()).data.chats;
+ const sortedMyChatList = sortChatList(myChats);
+
+ setMyHostChatList(filterPrivateChat(sortedMyChatList, true));
+ setMyChatList(filterPrivateChat(sortedMyChatList, false));
+ };
+ useEffect(() => {
+ getMyChat();
+ const timer = setInterval(() => {
+ getMyChat();
+ }, 30000);
+
+ return () => clearInterval(timer);
+ }, []);
+
+ return (
+
+
+
+
+
+ {myHostChatList.map(chat => (
+
+ ))}
+
+
+
+ {myChatList.map(chat => (
+
+ ))}
+
+
+
+ );
+}
+
+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/chat/[chatId].tsx b/pages/chat/[chatId].tsx
new file mode 100644
index 00000000..08a70689
--- /dev/null
+++ b/pages/chat/[chatId].tsx
@@ -0,0 +1,287 @@
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { io } from 'socket.io-client';
+import { useSetRecoilState } from 'recoil';
+import {
+ Chat,
+ IsValidAuth,
+ JoinersData,
+ LeaverData,
+ Message,
+} from '@/@types/types';
+import { useRouter } from 'next/router';
+import { getCookie } from 'cookies-next';
+import { GetServerSidePropsContext } from 'next';
+import {
+ ChatAlert,
+ ChatLoading,
+ ChatroomHeader,
+ EntryNotice,
+ ExitNotice,
+ MyMessage,
+ OtherMessage,
+} from '@/components/Chat';
+import authorizeFetch from '@/utils/authorizeFetch';
+import chatAPI from '@/apis/chatAPI';
+import styles from '@/components/Chat/Chat.module.scss';
+import showNavigationState from '@/stores/atoms/nav.atoms';
+
+interface MessageArray {
+ messages: Message[];
+}
+
+export default function Chatting({ authData }: IsValidAuth) {
+ const router = useRouter();
+ const { chatId } = router.query;
+ const currentChatId: string = Array.isArray(chatId)
+ ? chatId[0]
+ : chatId || '';
+
+ const [message, setMessage] = useState('');
+ const [messages, setMessages] = useState([]);
+ const [showAlert, setShowAlert] = useState(false);
+ const setShowNavigation = useSetRecoilState(showNavigationState);
+ useEffect(() => {
+ setShowNavigation(false);
+ return () => {
+ setShowNavigation(true);
+ };
+ }, []);
+
+ const messagesEndRef = useRef(null);
+
+ const [chatData, setChatData] = useState();
+
+ const [showEntryNotice, setShowEntryNotice] = useState(false);
+ const [showExitNotice, setShowExitNotice] = useState(false);
+
+ const [enterName, setEnterName] = useState('');
+ const [exitName, setExitName] = useState('');
+
+ const userId = authData.user.id;
+
+ const accessToken = getCookie('ACCESS_TOKEN');
+
+ const socket = useMemo(() => {
+ return io(`${process.env.NEXT_PUBLIC_CHAT_URL}?chatId=${chatId}`, {
+ extraHeaders: {
+ Authorization: `Bearer ${accessToken}`,
+ serverId: process.env.NEXT_PUBLIC_API_KEY!,
+ },
+ });
+ }, [currentChatId, accessToken]);
+
+ useEffect(() => {
+ const fetchChatData = async () => {
+ try {
+ const response = await chatAPI.getChatInfo(currentChatId);
+ const chatInfo: Chat = response.data.chat;
+ await setChatData(chatInfo);
+ } catch (error) {
+ console.error('Error fetching chat data:', error);
+ }
+ };
+
+ if (currentChatId) {
+ fetchChatData();
+ }
+ }, [currentChatId]);
+
+ useEffect(() => {
+ if (socket) {
+ socket.on('connect', () => {
+ console.log('Connected from server');
+ setTimeout(() => {
+ socket.emit('fetch-messages');
+ }, 500);
+ });
+ socket.on('messages-to-client', (messageArray: MessageArray) => {
+ setMessages(messageArray.messages);
+ });
+
+ socket.on('message-to-client', (messageObject: Message) => {
+ setMessages(prevMessages => [...prevMessages, messageObject]);
+ });
+
+ socket.on('join', async (messageObject: JoinersData) => {
+ console.log(messageObject, '채팅방 입장');
+ const joinUserInfo = await chatAPI.getUserInfo(
+ messageObject.joiners[0],
+ );
+
+ let userData = joinUserInfo.data.user;
+ setEnterName(userData.name);
+
+ // 이름 속성명 변경
+ userData = { ...userData, username: userData.name };
+ delete userData.name; // 기존의 이름 속성 삭제
+
+ setChatData((prevChatData): Chat => {
+ const updatedUsers = [...prevChatData!.users, userData];
+ return {
+ ...prevChatData!,
+ users: updatedUsers,
+ };
+ });
+ });
+
+ socket.on('leave', async (messageObject: LeaverData) => {
+ console.log(messageObject, '채팅방 퇴장');
+ const exitUserInfo = await chatAPI.getUserInfo(messageObject.leaver);
+ const userData = exitUserInfo.data.user;
+ setExitName(userData.name);
+
+ setChatData((prevChatData): Chat => {
+ const updatedUsers = prevChatData!.users.filter(
+ user => user.id !== userData.id,
+ );
+ return {
+ ...prevChatData!,
+ users: updatedUsers,
+ };
+ });
+ });
+ }
+
+ return () => {
+ socket.off('connect');
+ socket.off('messages-to-client');
+ socket.off('message-to-client');
+ socket.off('join');
+ socket.off('leave');
+ socket.disconnect();
+ };
+ }, [socket]);
+
+ useEffect(() => {
+ if (enterName) {
+ setShowEntryNotice(true);
+ }
+ const entryTimer = setTimeout(() => {
+ setShowEntryNotice(false);
+ setEnterName('');
+ }, 3000);
+
+ return () => clearTimeout(entryTimer);
+ }, [enterName]);
+
+ useEffect(() => {
+ if (exitName) {
+ setShowExitNotice(true);
+ }
+ const exitTimer = setTimeout(() => {
+ setShowExitNotice(false);
+ setExitName('');
+ }, 3000);
+
+ return () => clearTimeout(exitTimer);
+ }, [exitName]);
+
+ const scrollToBottom = () => {
+ if (messagesEndRef.current) {
+ messagesEndRef.current.scrollIntoView({
+ behavior: 'smooth',
+ });
+ }
+ };
+
+ useEffect(() => {
+ setTimeout(() => {
+ scrollToBottom();
+ }, 500);
+ }, [messages, showEntryNotice, showExitNotice]);
+
+ const handleSendMessage = (event: React.FormEvent) => {
+ event.preventDefault();
+
+ if (!message.trim()) {
+ setShowAlert(true);
+ setTimeout(() => {
+ setShowAlert(false);
+ }, 3000);
+ return;
+ }
+
+ socket.emit('message-to-server', message);
+ setMessage('');
+ };
+
+ return (
+ <>
+ {chatData && }
+
+
+ {!chatData ? (
+
+ ) : (
+ <>
+
+ {messages.map((msg, index) => {
+ const prevUserId =
+ index > 0 ? messages[index - 1].userId : null;
+ return msg.userId === userId ? (
+
+ ) : (
+
+ );
+ })}
+
+ {showEntryNotice &&
}
+ {showExitNotice &&
}
+ {showAlert &&
}
+ >
+ )}
+
+
+
+ {chatData ? (
+
+ ) : null}
+ >
+ );
+}
+
+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: { authData: response.data },
+ };
+ }
+ if (!refreshToken) {
+ // accessToken이 없으면 로그인 페이지로 리다이렉트
+ return {
+ redirect: {
+ destination: '/login',
+ permanent: false,
+ },
+ };
+ }
+ return {
+ props: {},
+ };
+};
diff --git a/pages/index.tsx b/pages/index.tsx
new file mode 100644
index 00000000..2a0d1b33
--- /dev/null
+++ b/pages/index.tsx
@@ -0,0 +1,42 @@
+import Header from '@/components/Common/Header/Header';
+import HostList from '@/components/HostList/HostList';
+import authorizeFetch from '@/utils/authorizeFetch';
+import { GetServerSidePropsContext } from 'next';
+
+export default function Home() {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+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: { authData: response.data },
+ };
+ }
+ if (!refreshToken) {
+ // accessToken이 없으면 로그인 페이지로 리다이렉트
+ return {
+ redirect: {
+ destination: '/login',
+ permanent: false,
+ },
+ };
+ }
+ return {
+ props: {},
+ };
+};
diff --git a/pages/login.tsx b/pages/login.tsx
new file mode 100644
index 00000000..41ca57a6
--- /dev/null
+++ b/pages/login.tsx
@@ -0,0 +1,136 @@
+import axios from 'axios';
+import React, { useEffect } from 'react';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { FieldValues, SubmitHandler, useForm } from 'react-hook-form';
+import { useSetRecoilState } from 'recoil';
+import { GetServerSidePropsContext } from 'next';
+import { setToken } from '@/utils/tokenManager';
+import userTokenState from '@/stores/atoms/userTokenState';
+import styles from '@/components/login.module.scss';
+import showNavigationState from '@/stores/atoms/nav.atoms';
+
+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 function Login() {
+ const {
+ register,
+ handleSubmit,
+ formState: { isSubmitting },
+ } = useForm();
+
+ const router = useRouter();
+ const setUser = useSetRecoilState(userTokenState);
+
+ const handleLoginClick: SubmitHandler = async data => {
+ const loginId = data.id.trim() as string;
+ const password = data.password.trim() as string;
+ if (loginId && password) {
+ try {
+ // await axios.post('/api/login', { id, password });
+ const res = await instance.post('/login', {
+ id: loginId,
+ password,
+ });
+ const { accessToken, refreshToken } = res.data;
+
+ setToken('ACCESS_TOKEN', accessToken);
+ setToken('REFRESH_TOKEN', refreshToken);
+
+ const userResponse = await axios.get(
+ 'https://fastcampus-chat.net/auth/me',
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ serverId: 'cb7fb111e',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ },
+ );
+
+ const { id, name, picture } = userResponse.data.user;
+ setUser({ id, name, picture, isLoggedIn: true });
+ router.push('/');
+ } catch (error) {
+ if (axios.isAxiosError(error)) {
+ if (error.response?.status === 400) {
+ alert('잘못된 아이디 또는 비밀번호입니다.');
+ }
+ }
+ }
+ } else {
+ alert('아이디 또는 비밀번호를 입력하세요.');
+ }
+ };
+ const setShowNavigation = useSetRecoilState(showNavigationState);
+ useEffect(() => {
+ setShowNavigation(false);
+ return () => {
+ setShowNavigation(true);
+ };
+ }, []);
+
+ 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 (
+
+ );
+}
+
+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 (
+ <>
+
+ {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');
+}