diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..f47b5ce
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,4 @@
+node_modules
+public
+build
+dist
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..2e9bcc1
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,109 @@
+module.exports = {
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:prettier/recommended',
+ 'plugin:import/errors',
+ 'plugin:import/warnings',
+ 'plugin:import/typescript',
+ ],
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ plugins: [
+ '@typescript-eslint',
+ 'react',
+ 'prettier',
+ 'import',
+ 'import-order',
+ 'react-hooks',
+ ],
+ rules: {
+ 'no-console': ['warn', { allow: ['warn', 'error'] }],
+ 'react/function-component-definition': [
+ 2,
+ {
+ namedComponents: 'arrow-function',
+ unnamedComponents: 'arrow-function',
+ },
+ ],
+
+ 'arrow-body-style': ['error', 'as-needed'],
+
+ 'react/jsx-pascal-case': 'error',
+
+ camelcase: ['error', { properties: 'never' }],
+ 'react/jsx-handler-names': [
+ 'error',
+ {
+ eventHandlerPropPrefix: 'on',
+ },
+ ],
+ 'import/no-default-export': 'off',
+ 'import/prefer-default-export': 'off',
+ 'import/no-unresolved': 'off',
+ 'react-hooks/rules-of-hooks': 'error',
+ 'react/prop-types': 'warn',
+ 'prefer-const': 'error',
+ 'no-var': 'error',
+ 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
+ eqeqeq: 'error',
+ 'prefer-destructuring': [
+ 'error',
+ {
+ array: false,
+ object: true,
+ },
+ ],
+ 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }],
+ '@typescript-eslint/no-unused-vars': [
+ 'warn',
+ {
+ varsIgnorePattern: '^_',
+ argsIgnorePattern: '^_',
+ },
+ ],
+ '@typescript-eslint/no-explicit-any': 'warn',
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ 'no-useless-rename': 'error',
+ 'object-shorthand': 'error',
+ 'react/jsx-key': ['error', { checkFragmentShorthand: true }],
+ 'react/react-in-jsx-scope': 'off',
+ 'prettier/prettier': ['error', { endOfLine: 'auto' }],
+ 'import/order': [
+ 'error',
+ {
+ groups: ['builtin', 'external', 'internal', 'index'],
+ pathGroups: [
+ {
+ pattern: 'react',
+ group: 'external',
+ position: 'before',
+ },
+ ],
+ pathGroupsExcludedImportTypes: ['react'],
+ alphabetize: {
+ order: 'asc',
+ caseInsensitive: true,
+ },
+ },
+ ],
+ 'react/jsx-handler-names': 'off',
+ },
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ 'import/resolver': {
+ node: {
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ paths: ['src'],
+ },
+ },
+ },
+};
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
new file mode 100644
index 0000000..4debfd2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -0,0 +1,45 @@
+name: ๐ bug
+description: ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ๋์?
+title: '[bug] '
+labels: ['๐ Bug']
+projects: []
+assignees:
+ -
+body:
+ - type: textarea
+ id: bug-description
+ attributes:
+ label: ๐ ์ค๋ช
+ description: ๋ฒ๊ทธ์ ๋ํ ์ค๋ช
์ ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: true
+ - type: textarea
+ id: bug-simulation
+ attributes:
+ label: ๐ป ๋ฒ๊ทธ ์๋ฎฌ๋ ์ด์
+ description: ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ ์ํฉ์ ๋จ๊ณ๋ณ๋ก ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: true
+ - type: textarea
+ id: bug-log
+ attributes:
+ label: ๐ ๋ก๊ทธ
+ description: ๋ก๊ทธ๊ฐ ์๋ค๋ฉด ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: false
+ - type: textarea
+ id: bug-os
+ attributes:
+ label: ๐ ํ๊ฒฝ
+ description: ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ํ๊ฒฝ์ ๋ํด ์์ฑํด ์ฃผ์ธ์.
+ placeholder: |
+ OS: macOS 14.5
+ validations:
+ required: false
+ - type: textarea
+ id: bug-more
+ attributes:
+ label: ๐ ๋ฉ๋ชจ
+ description: ๋ ํ๊ณ ์ถ์ ๋ง์ด ์๋ค๋ฉด ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
new file mode 100644
index 0000000..563e1f5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -0,0 +1,28 @@
+name: โจ feature
+description: ์๋ก์ด ๊ธฐ๋ฅ์ด๋ ๋ณ๊ฒฝ์ฌํญ์ด ์๋์?
+title: '[feature] '
+labels: ['โจ Feature']
+body:
+ - type: textarea
+ id: feature-description
+ attributes:
+ label: ๐ ์ค๋ช
+ description: ์๋ก์ด ๊ธฐ๋ฅ์ ๋ํ ์ค๋ช
์ ์์ฑํด ์ฃผ์ธ์.
+ placeholder: ์์ธํ๊ฒ ์์ฑํ๊ธฐ!
+ validations:
+ required: true
+ - type: textarea
+ id: feature-todo
+ attributes:
+ label: โ
ํด์ผ ํ ์ผ
+ description: ํด์ผ ํ ์ผ์ ๋ํ Tasks๋ฅผ ์์ฑํด ์ฃผ์ธ์.
+ placeholder: ์ต๋ํ ์ธ๋ถํํด์ ์์ฑ!
+ validations:
+ required: true
+ - type: textarea
+ id: feature-more
+ attributes:
+ label: ๐ ๋ฉ๋ชจ
+ description: ๋ ํ๊ณ ์ถ์ ๋ง์ด ์๋ค๋ฉด ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/help.yml b/.github/ISSUE_TEMPLATE/help.yml
new file mode 100644
index 0000000..796f5e6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/help.yml
@@ -0,0 +1,36 @@
+name: ๐จ help
+description: ๋์์ด ํ์ํ์ ๊ฐ์?
+title: '[help] '
+labels: ['๐จ Help']
+body:
+ - type: textarea
+ id: help-description
+ attributes:
+ label: ๐จ ๋ฌธ์ ์ค๋ช
+ description: ๋์์ด ํ์ํ ๋ฌธ์ ์ ๋ํ ์ค๋ช
์ ์์ฑํด ์ฃผ์ธ์.
+ placeholder: ์์ธํ๊ฒ ์์ฑํ๊ธฐ!
+ validations:
+ required: true
+ - type: textarea
+ id: help-context
+ attributes:
+ label: ๐ ๋ฌธ์ ๋ฐ์ ์ํฉ
+ description: ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ํฉ์ ์ค๋ช
ํด ์ฃผ์ธ์.
+ placeholder: ๊ฐ๋ฅํ ๊ตฌ์ฒด์ ์ผ๋ก ์์ฑํ๊ธฐ!
+ validations:
+ required: true
+ - type: textarea
+ id: help-attempts
+ attributes:
+ label: ๐ก ์๋ํ ํด๊ฒฐ์ฑ
+ description: ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๋ํ ๋ฐฉ๋ฒ์ด ์๋ค๋ฉด ์์ฑํด ์ฃผ์ธ์.
+ placeholder: ์๋ํ ๋ฐฉ๋ฒ๋ค ์์ฑ!
+ validations:
+ required: false
+ - type: textarea
+ id: help-more
+ attributes:
+ label: ๐ ์ถ๊ฐ ๋ฉ๋ชจ
+ description: ์ถ๊ฐ๋ก ํ๊ณ ์ถ์ ๋ง์ด ์๋ค๋ฉด ์์ฑํด ์ฃผ์ธ์.
+ validations:
+ required: false
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..9df6e36
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,19 @@
+# ๐ ํ ๋ฆฌํ์คํธ ์ ์
+
+**[์์
๋ด์ฉ์ ๊ฐ๋ตํ ์ ์ด์ฃผ์ธ์]**
+
+## ๐ ์์
๋ด์ฉ
+
+์์ ํ ๋ด์ฉ์ด๋ ์ถ๊ฐํ ๊ธฐ๋ฅ์ ๋ํด ์์ธํ ์ค๋ช
ํด ์ฃผ์ธ์.
+
+## ๐ง ๋ณ๊ฒฝ ์ฌํญ
+
+์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์์ฝํด ์ฃผ์ธ์.
+
+## ๐ธ ์คํฌ๋ฆฐ์ท (์ ํ ์ฌํญ)
+
+์์ ๋ ํ๋ฉด ๋๋ ๊ธฐ๋ฅ์ ์์ฐํ ์ ์๋ ์คํฌ๋ฆฐ์ท์ ์ฒจ๋ถํ ์ ์์ต๋๋ค.
+
+## ๐ ๊ธฐํ
+
+์ถ๊ฐ์ ์ผ๋ก ์ ๋ฌํ๊ณ ์ถ์ ๋ด์ฉ์ด๋ ํน๋ณํ ์๊ตฌ ์ฌํญ์ด ์์ผ๋ฉด ์์ฑํด ์ฃผ์ธ์.
diff --git a/.gitignore b/.gitignore
index a547bf3..2c72ae6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,11 @@ dist
dist-ssr
*.local
+.last-run.json
+
+#firebase config data
+.env
+
# Editor directories and files
.vscode/*
!.vscode/extensions.json
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..d24fdfc
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+npx lint-staged
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..76add87
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,2 @@
+node_modules
+dist
\ No newline at end of file
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
new file mode 100644
index 0000000..dcedfb9
--- /dev/null
+++ b/.prettierrc.cjs
@@ -0,0 +1,13 @@
+module.exports = {
+ singleQuote: true,
+ trailingComma: 'es5',
+ tabWidth: 2,
+ semi: true,
+ useTabs: false,
+ printWidth: 80,
+ endOfLine: 'auto',
+ arrowParens: 'always',
+ jsxBracketSameLine: false,
+ jsxSingleQuote: true,
+ proseWrap: 'preserve',
+};
diff --git a/README.md b/README.md
index 74872fd..2262ec4 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,368 @@
-# React + TypeScript + Vite
+# โถ๏ธ ํ๋ ์ด๋ฆฌ์คํธ ๊ณต์ SNS ์ํ๋ฆฌ(Weply)
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+
+
-Currently, two official plugins are available:
+### ๐ ํ
์คํธ ๊ณ์
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+- ID : test1234
+- PW : test1234
-## Expanding the ESLint configuration
+## 1. ํ๋ก์ ํธ ์๊ฐ
-If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
+์ํ๋ฆฌ(Weply)๋ ์ฌ์ฉ์๋ค์ด ๋ง๋ ์์ ๋ง์ ์์ ๋งํฌ ๊ธฐ๋ฐ(์ ํ๋ธ, Vimeo, SoundCloud) ํ๋ ์ด ๋ฆฌ์คํธ๋ฅผ ๊ณต์ ํ๊ณ , ๊ตฌ๋
ํ์ฌ ์์ ๋ง์ ํ์ ๋ผ์ธ์ ๋ง๋ค๊ณ ๋คํธ์ํน ํ ์ ์๋ SNS ์๋น์ค ์
๋๋ค
-- Configure the top-level `parserOptions` property like this:
+
+
+## 2. ํ์ ์๊ฐ
+
+
+
+| **๊ถํ์ง** | **๊น๋์** | **๊น์ฑํ** | **๊น์๋ฏผ** | **์ด๋ํ** |
+| :--------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: |
+| [
@hyeppyy](https://github.com/hyeppyy) | [
@dev-meryoung](https://github.com/dev-meryoung) | [
@kimisadev27](https://github.com/kimisadev27) | [
@ssuminii](https://github.com/ssuminii) | [
@LfromTheE](https://github.com/LfromTheE) |
+
+
+
+
+
+## 3. ํ๋ก์ ํธ ๊ธฐ๊ฐ
+
+### ๐
์ ์ฒด ๊ฐ๋ฐ ๊ธฐ๊ฐ
+
+- ์ค๊ณ ๋ฐ ๋์์ธ, ํ๋ก์ ํธ ์ธํ
: 2024. 08. 19. ~ 2024. 08. 25.
+- UI ๋ฐ ์ปดํฌ๋ํธ ๊ตฌํ : 2024. 08. 26. ~ 2024. 08. 30.
+- ๊ธฐ๋ฅ ๊ตฌํ : 2024. 08. 31. ~ 2024. 09. 09.
+
+
+
+## 4. ๊ธฐ์ ์คํ ๋ฐ ๊ฐ๋ฐํ๊ฒฝ
+
+| ๊ธฐ์ ์คํ | ๋์
์ด์ |
+| -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
+|  | ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์ํคํ
์ฒ๋ก ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ๋๊ณ ๊ฐ์ DOM์ ์ฌ์ฉํ์ฌ ํจ์จ์ ์ธ ๋ ๋๋ง ์ ๊ณต ๊ฐ๋ฅ |
+|  | ์ ์ ํ์
๊ฒ์ฌ๋ก ์ฝ๋ ์์ ์ฑ์ ํฅ์์ํค๊ณ ๊ฐ๋ฐ ์ ์ค๋ฅ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ |
+|  | ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๋จ์ํ๊ณ ์ง๊ด์ ์ผ๋ก ๊ด๋ฆฌ ๋ฐ ์ฒ๋ฆฌ |
+|  | ๋น๋๊ธฐ ๋ฐ์ดํฐ ์์ฒญ ๋ฐ ์บ์ฑ ๊ธฐ๋ฅ์ผ๋ก ํจ์จ์ ์ธ ์๋ฒ ๋ฐ์ดํฐ ๊ด๋ฆฌ |
+|  | ๋น ๋ฅธ ๊ฐ๋ฐ ์๋ฒ ์์ ๋ฐ ๋น๋ ์๊ฐ ์ ๊ณต ๊ฐ๋ฅ |
+|  | ์๋ฒ๋ฆฌ์ค ์ํคํ
์ฒ๋ก ๋ฐฑ์๋ ๊ฐ๋ฐ ์๊ฐ ๋จ์ถ ๊ฐ๋ฅ |
+|  | CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์คํ์ผ๋ง ๊ฐ๋ฅ, ๋์ ์คํ์ผ๋ง ์ฉ์ด |
+
+
+
+## 5. ์ญํ ๋ถ๋ด
+
+### โผ๏ธ ๊ถํ์ง
+
+- UI
+ - ๊ณตํต ์ปดํฌ๋ํธ : ๋๊ธ, ์ผ๋ฐฅ ๋ฒํผ, ๋ชจ๋ฌ, ๋ก๋ฉ ์คํผ๋, ํญ ๋ฒํผ, ํ๋ ์ด๋ฆฌ์คํธ ์นด๋
+ - ํ์ด์ง : ๋ง์ดํ๋ฆฌ(ํ, ์ ์ฅ๋ ํ๋ฆฌ, ์ข์์), 404
+- ๊ธฐ๋ฅ ๊ตฌํ
+ - ๋ฌดํ ์คํฌ๋กค
+ - ํ๋ ์ด๋ฆฌ์คํธ ๊ณต๊ฐ์ฌ๋ถ ๋ณ๊ฒฝ
+ - ํ์ด์ง๋ณ ํํฐ๋ง(๊ณต๊ฐ์ฌ๋ถ, ์ ๋ ฌ)
+- ๊ทธ ์ธ
+ - ์ด๊ธฐ ์คํ์ผ ์ธํ
+ - UI ๋์์ธ
+ - ๋ก๊ทธ์ธ ํ
์คํธ
+
+### โผ๏ธ ๊น๋์
+
+- UI
+ - ๊ณตํต ์ปดํฌ๋ํธ : ๋ฒํผ, ์์ด์ฝ ๋ฒํผ, ํ
์คํธ ํํฐ, ํ์
ํํฐ, ๋ก๊ณ , ๋ค๋น๊ฒ์ด์
๋ฐ
+ - ํ์ด์ง : ํ(๋ฉ์ธ), ๊ฒ์ ๊ฒฐ๊ณผ
+- ๊ธฐ๋ฅ ๊ตฌํ
+ - ๊ฒ์ ํค์๋(์ ๋ชฉ, ํด์ํ๊ทธ)์ ๋ฐ๋ฅธ ๊ฒ์ ๊ฒฐ๊ณผ ์ถ๋ ฅ
+ - ํ์ด์ง๋ณ ํํฐ๋ง(์ ๋ ฌ)
+ - ํ๋ ์ด๋ฆฌ์คํธ ์ข์์ ๋ฐ ์ ์ฅ ๊ธฐ๋ฅ ๊ตฌํ
+- ๊ทธ ์ธ
+ - ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ
+ - ๋ก๊ณ ๋์์ธ
+
+### โผ๏ธ ๊น์ฑํ
+
+- UI
+ - ๊ณตํต ์ปดํฌ๋ํธ : ํ ์คํธ ๋ฉ์์ง, ๋ฏธ๋ ํ๋ ์ด๋ฆฌ์คํธ, ํ ๊ธ, ๊ฐ๋ณ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์
+ - ํ์ด์ง : ํ๋ ์ด๋ฆฌ์คํธ ์์ธ, ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ ๋ฐ ์์
+- ๊ธฐ๋ฅ ๊ตฌํ
+ - ์ ํ๋ธ, ๋น๋ฉ์ค, ์ฌ์ด๋ํด๋ผ์ฐ๋ ๋งํฌ ๋ณํ
+ - ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ, ์์ , ์์ธ ์กฐํ
+- ๊ทธ ์ธ
+ - ํ์ด์ง๋ณ ํค๋ ์กฐ๊ฑด๋ถ ๋ ๋๋ง ์ฒ๋ฆฌ
+ - ๊ธฐ๋ฅ์ ์์ ์์ฑ
+ - DB ๊ตฌ์กฐ ์ค๊ณ
+
+### โผ๏ธ ๊น์๋ฏผ
+
+- UI
+ - ๊ณตํต ์ปดํฌ๋ํธ : ์ธํ๋ฐ์ค, ํ๋กํ, ํด์ํ๊ทธ, ์ฒดํฌ๋ฐ์ค
+ - ํ์ด์ง : ๋ก๊ทธ์ธ, ํ์๊ฐ์
, ํด์ํ๊ทธ ์ ํ, ๋ง์ดํ์ด์ง, ํ๋กํ ์์
+- ๊ธฐ๋ฅ ๊ตฌํ
+ - ๋ก๊ทธ์ธ, ํ์๊ฐ์
์ ์ ํจ์ฑ ๋ฐ ์ค๋ณต ๊ฒ์ฌ
+ - ํด์ํ๊ทธ ์ ํ
+ - ๋ด๊ฐ ์ด ๋๊ธ ์กฐํ ๋ฐ ์ญ์
+- ๊ทธ ์ธ
+ - UI ๋์์ธ
+
+### โผ๏ธ ์ด๋ํ
+
+- UI
+ - ๊ณตํต ์ปดํฌ๋ํธ : ๋ ์ด์์(ํค๋, ๋ค๋น๊ฒ์ด์
๋ฐ)
+ - ํ์ด์ง : ์ธ๊ธฐ, ํ๋ก์, ํ๋ก์ ๋ฐ ํ๋ก์ ๋ชฉ๋ก
+- ๊ธฐ๋ฅ ๊ตฌํ
+ - ์ ํธ ํด์ํ๊ทธ ๊ธฐ์ค ํํฐ๋ง
+ - ํ๋ก์ ์ฑ๋ ๊ธฐ์ค ํํฐ๋ง
+ - ํ๋ก์ ๋ฐ ํ๋ก์ ์กฐํ ๋ฐ ์ญ์
+- ๊ทธ ์ธ
+ - DB ๊ตฌ์กฐ ์ค๊ณ
+ - ํ์ด์ด๋ฒ ์ด์ค ๋ฐ์ดํฐ ๊ด๋ฆฌ
+ - ๋ฐํ
+
+
+
+## 6. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ
+
+
+
+
+
+## 7. ๊ธฐ๋ฅ ์๊ฐ
+
+### ํ์๊ฐ์
ํ์ด์ง
+
+- ๋ก๊ทธ์ธ ํ๋ฉด์์ **ํ์๊ฐ์
ํ๊ธฐ**๋ฅผ ํด๋ฆญํ๋ฉด ํ์๊ฐ์
ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
+- ์์ด๋์ ์ฑ๋ ์ด๋ฆ์ ์ค๋ณต ๊ฒ์ฌ๋ฅผ ํตํด ์ฌ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
+- ๋ชจ๋ ์
๋ ฅ ํญ๋ชฉ์ด ์ ํจํ๊ณ ์ค๋ณต ๊ฒ์ฌ๊ฐ ํต๊ณผ๋๋ฉด ํ์๊ฐ์
๋ฒํผ์ด ํ์ฑํ๋ฉ๋๋ค.
+- ํ์๊ฐ์
์ด ์๋ฃ๋๋ฉด ์๋์ผ๋ก ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋๋ ์
๋ฉ๋๋ค.
+
+| ํ์๊ฐ์
|
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+### ๋ก๊ทธ์ธ ํ์ด์ง
+
+- ๋นํ์์ ํ, ์ธ๊ธฐ, ๊ฒ์, ์์ธํ์ด์ง์๋ง ์ ๊ทผํ ์ ์์ผ๋ฉฐ ์ด ์ธ์ ํ์ด์ง์ ์ ๊ทผํ ์ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋๋ฉ๋๋ค.
+- ์ฌ์ฉ์๋ ์์ด๋ ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋ฉฐ ๋๋ ๊ตฌ๊ธ๋ก ๋ก๊ทธ์ธ ๋ฒํผ์ผ๋ก ๊ตฌ๊ธ ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.
+- ์ฌ์ฉ์๋ **๋ก๊ทธ์ธ ์ ๋ณด ๊ธฐ์ตํ๊ธฐ** ํ ๊ธ์ ํตํด ์ฌ๋ก๊ทธ์ธ์ ์์ด๋๋ฅผ ์ ์ฅํ ์ ์์ต๋๋ค.
+- ์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์๋ชป๋์์ ๊ฒฝ์ฐ ๊ฒฝ๊ณ ๋ฉ์ธ์ง๊ฐ ํ์๋ฉ๋๋ค.
+- ๋ก๊ทธ์ธ ํ ํํ์ด์ง๋ก ๋ฆฌ๋๋ ์
๋ฉ๋๋ค.
+
+| ์์ด๋ ๋ก๊ทธ์ธ | ๊ตฌ๊ธ ๋ก๊ทธ์ธ |
+| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
+|
|
|
+
+### ํด์ํ๊ทธ ์ ํ ํ์ด์ง
+
+- ํ์๊ฐ์
ํ ์ฌ์ฉ์๊ฐ **์ฒ์ ๋ก๊ทธ์ธ**ํ๋ ๊ฒฝ์ฐ ํด์ํ๊ทธ ์ ํ ํ์ด์ง๋ก ๋ฆฌ๋๋ ์
๋ฉ๋๋ค.
+- ์ฌ์ฉ์๊ฐ ๊ด์ฌ ์๋ ํด์ํ๊ทธ๋ฅผ ์ ํํ๋ฉด NEXT ๋ฒํผ์ ํตํด ํํ์ด์ง๋ก ์ด๋ํ๊ฒ ๋ฉ๋๋ค. (ํด์ํ๊ทธ ์ ํ์ 10๊ฐ๋ก ์ ํ๋ฉ๋๋ค.)
+- ํด์ํ๊ทธ๋ฅผ ๊ณ ๋ฅด์ง ์๊ณ SKIP์ผ๋ก ๋์ด๊ฐ ์ ์์ต๋๋ค.
+- ๊ณ ๋ฅธ ํด์ํ๊ทธ๋ค์ ์ธ๊ธฐ ํ์ด์ง์ ๋ฐ์๋ฉ๋๋ค.
+- ์ถํ ๋ง์ดํ์ด์ง์์ ์์ ํ ์ ์์ต๋๋ค.
+
+| ํด์ํ๊ทธ ์ ํ |
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+### ํํ์ด์ง
+
+- ์ฌ์ฉ์๋ ์ฐ์ธก ์๋จ์ ํญ์ ํตํด ์ต์ ์, ์ข์์์, ๋๊ธ์์ผ๋ก ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ ๋ ฌํ ์ ์์ต๋๋ค.
+- ๋ฌดํ ์คํฌ๋กค ๊ธฐ๋ฅ์ผ๋ก ์ฌ์ฉ์๊ฐ ํ์ด์ง ํ๋จ์ ๋๋ฌํ๋ฉด ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
+- ํ๋ ์ด๋ฆฌ์คํธ ์ข์์๊ฐ ๊ฐ๋ฅํ๋ฉฐ ํ๋ ์ด๋ฆฌ์คํ ๋๋ฅผ ์ ํ๋ ์ด๋ฆฌ์คํธ ์์ธํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
+
+| ํํ์ด์ง |
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+### ์์ ์์ธํ์ด์ง
+
+- ์์ ์์ธํ์ด์ง๋ ์ฌ์ฉ์๊ฐ ์์์ ์์ฒญํ๊ณ , ํ๊ฐํ๋ฉฐ, ๋๊ธ๋ก ์ํตํ๊ณ , ๊ตฌ๋
๋ฐ ๊ณต์ ํ ์ ์๋ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํ์ด์ง์
๋๋ค.
+
+| ์ข์์ | ๊ณต์ | ์ ์ฅ | ํ๋ก์ฐ |
+| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
+|
|
|
|
|
+
+[์ข์์]
+
+- ์ข์์ ๋ฒํผ ํด๋ฆญ ์ ํด๋น ์์์ด ๋ค์ด์๋ ํ๋ ์ด๋ฆฌ์คํธ์ ์ข์์ ์๊ฐ ์ฌ๋ผ๊ฐ๋๋ค. ๋ง์ดํ๋ฆฌ ์ข์์ ํญ์์ ๋ด๊ฐ ์ข์์ํ ํ๋ ์ด๋ฆฌ์คํธ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ต๋๋ค.
+
+[๊ณต์ ]
+
+- ๊ณต์ ๋ฒํผ ํด๋ฆญ ์ ํ๋ ์ด๋ฆฌ์คํธ์ URL์ด ๋ณต์ฌ๋๊ณ '๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค'๋ผ๋ ํ ์คํธ๊ฐ ๋ํ๋ฉ๋๋ค.
+- ๋ณต์ฌ๋ URL์ ๊ณต์ ํ์ฌ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ์์์ ๋๋ ์ ์์ต๋๋ค.
+
+[์ ์ฅ]
+
+- ์ฌ์ฉ์๊ฐ ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์์ ๋ง์ ์ฌ์๋ชฉ๋ก(๋ง์ดํ๋ฆฌ)์ ์ ์ฅํ์ฌ ๋์ค์ ๋ค์ ๋ณผ ์ ์๋๋ก ํฉ๋๋ค.
+
+[ํ๋ก์ฐ]
+
+- ์ฌ์ฉ์๋ ํด๋น ์ฑ๋์ ํ๋ก์ฐํ์ฌ ํ๋ก์ ํ์ด์ง์์ ํ๋ก์ฐํ ์ฑ๋์ ์ ์ฝํ
์ธ ๋ฅผ ๋ ์ฝ๊ฒ ์ ํ ์ ์์ต๋๋ค.
+
+| ๋๊ธ ์ถ๊ฐ ๋ฐ ์ญ์ | ๋ฏธ๋ ํ๋ ์ด๋ฆฌ์คํธ ํ์ธ |
+| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
+|
|
|
+
+[๋๊ธ ์ถ๊ฐ ๋ฐ ์ญ์ ]
+
+- ์ฌ๋๋ค์ด ํด๋น ์์์ ๋ํด ์๊ฒฌ์ ๋จ๊ธฐ๊ฑฐ๋ ๋ค๋ฅธ ์ฌ์ฉ์๋ค๊ณผ ์ํตํ ์ ์๋ ๊ณต๊ฐ์
๋๋ค. ๋ด๊ฐ ์ด ๋๊ธ์ ์ญ์ ๋ฒํผ์ ๋๋ฌ ์ญ์ ํ ์ ์์ต๋๋ค.
+
+[๋ฏธ๋ ํ๋ ์ด๋ฆฌ์คํธ ํ์ธ]
+
+- ์ฑ๋ ์ ๋ณด์์ X ํ์๋ฅผ ๋๋ฅด๋ฉด ์ฑ๋ ์ ๋ณด๊ฐ ํ๋จ์ ์๊ฒ ๋จ๊ฒ ํ์ฌ ์์์ ๋์ฑ ์ง์คํ ์ ์๋๋ก ํฉ๋๋ค.
+
+### ์ธ๊ธฐ ํ์ด์ง
+
+- ํ์๊ฐ์
์ ์ ํํ ํด์ํ๊ทธ๋ฅผ ๋ฐํ์ผ๋ก ๊ด๋ จ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
+- ๋ง์ดํ์ด์ง์์ ํด์ํ๊ทธ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋ณ๊ฒฝํ ํด์ํ๊ทธ๋ก ์ธ๊ธฐ ํ์ด์ง ๋ฆฌ์คํธ๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
+- ์ธ๊ธฐ๊ธ์์น ๋์์์ ๊ณ ์ ์ด๋ฉฐ, ๋๋จธ์ง 3๊ฐ์ ํ๊ทธ๊ฐ ์ฌ์ฉ์ ์ ํ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ณต๋ฉ๋๋ค.
+- ์ ํํ ํด์ํ๊ทธ๊ฐ ์์ ๊ฒฝ์ฐ ๋๋ค์ผ๋ก ์ ๊ณต๋ฉ๋๋ค.
+
+ | ์ธ๊ธฐ ํ์ด์ง |
+ | --------------------------------------------------------------------------------------------------------------- |
+ |
|
+
+### ๋ง์ดํ๋ฆฌ ํ์ด์ง
+
+- ๋ง์ดํ๋ฆฌ ํ์ด์ง๋ ์ฑ๋ ์ ๋ณด๋ฅผ ํ์ธํ๊ณ , ๋ด๊ฐ ์์ฑ/์ ์ฅ/์ข์์ํ ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ํ์ด์ง์
๋๋ค. ์ฑ๋ ์ ๋ณด๋ ํ๋กํ ์ด๋ฏธ์ง, ์ฑ๋๋ช
, ํ๋ก์์, ํ๋ก์์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
+
+#### ๋ง์ดํ๋ฆฌ ํญ
+
+- ๋ง์ดํ๋ฆฌ ํํ์ด์ง์์๋ ๋ด๊ฐ ์์ฑํ ํ๋ ์ด๋ฆฌ์คํธ ๋ชฉ๋ก์ ํ์ธ, ์์ , ์ญ์ ํ ์ ์์ต๋๋ค. ๋ํ ํ๋ ์ด๋ฆฌ์คํธ์ ๊ณต๊ฐ์ฌ๋ถ๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
+
+| ํํฐ๋ง | ๊ณต๊ฐ์ฌ๋ถ ๋ณ๊ฒฝ | ์์ | ์ญ์ |
+| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
+|
|
|
|
|
+
+[ํํฐ๋ง]
+
+- ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ต์ ์, ์ข์์์, ๋๊ธ์์ผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
+- ํ๋ ์ด๋ฆฌ์คํธ์ ๊ณต๊ฐ์ฌ๋ถ(์ ์ฒด/๊ณต๊ฐ/๋น๊ณต๊ฐ)๋ฅผ ํํฐ๋งํ ์ ์์ต๋๋ค.
+
+[๊ณต๊ฐ์ฌ๋ถ ๋ณ๊ฒฝ]
+
+- ์๋ฌผ์ ์์ด์ฝ์ ํด๋ฆญํ์ฌ ๊ณต๊ฐ/๋น๊ณต๊ฐ ์ฌ๋ถ๋ฅผ ์ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ๋น๊ณต๊ฐ๋ก ์ ํ๋ ํ๋ ์ด๋ฆฌ์คํธ๋ ํ์ด๋, ์ธ๊ธฐ ํ์ด์ง์์ ํ์ธํ ์ ์์ต๋๋ค.
+
+[์์ ]
+
+- ์ ์ธ๊ฐ ์์ด์ฝ ํด๋ฆญ ํ ์์ ๋ฒํผ์ ๋๋ฅด๋ฉด ํด๋น ํ๋ ์ด๋ฆฌ์คํธ ์์ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
+
+[์ญ์ ]
+
+- ์ ์ธ๊ฐ ์์ด์ฝ ํด๋ฆญ ํ ์ญ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ญ์ ๋ชจ๋ฌ์ด ๋ํ๋ฉ๋๋ค.
+- ๋ชจ๋ฌ์์ ์ทจ์ ํด๋ฆญ ์ ๋ชจ๋ฌ์ด ๋ซํ๊ณ , ์ญ์ ํด๋ฆญ ์ ํด๋น ํ๋ ์ด๋ฆฌ์คํธ๊ฐ ์ญ์ ๋๊ณ 'ํ๋ ์ด๋ฆฌ์คํธ๊ฐ ์ญ์ ๋์์ต๋๋ค'๋ผ๋ ํ ์คํธ๊ฐ ๋ํ๋ฉ๋๋ค.
+
+#### ์ ์ฅ๋ ํ๋ฆฌ ํญ
+
+- ์ ์ฅํ ํ๋ฆฌ ํ์ด์ง์์๋ ๋ด๊ฐ ์ ์ฅ(ํ๋ฌ์ค ๋ฒํผ์ ๋๋ฅธ)ํ ํ๋ ์ด๋ฆฌ์คํธ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ต๋๋ค.
+
+| ํํฐ๋ง | ์ ์ฅ ์ทจ์ |
+| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
+|
|
|
+
+[ํํฐ๋ง]
+
+- ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ต์ ์, ์ข์์์, ๋๊ธ์์ผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
+
+[์ ์ฅ ์ทจ์]
+
+- ๋ฆฌ์คํธ ๋ชจ์ ์์ด์ฝ์ ํด๋ฆญํ์ฌ ํ๋ ์ด๋ฆฌ์คํธ ์ ์ฅ์ ์ทจ์ํ ์ ์์ต๋๋ค.
+- ์ค์๋ก ์ ์ฅ์ ์ทจ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋ ์์๋ง ์ ์ฅ ์ทจ์๊ฐ ๋ฐ์๋๋๋ก ๊ตฌํํ์ต๋๋ค.
+
+#### ์ข์์ ํญ
+
+- ์ข์์ ํ์ด์ง์์๋ ๋ด๊ฐ ์ข์์(ํํธ ๋ฒํผ์ ๋๋ฅธ)ํ ํ๋ ์ด๋ฆฌ์คํธ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ต๋๋ค.
+- ์ข์์ํญ์ ๋์๊ฒํ ๋ณด์ด๋ฉฐ, ๋ค๋ฅธ ์ฌ๋์ ํ์ด์ง์์๋ ๋ณด์ด์ง ์์ ์์ ๋กญ๊ฒ ๋ชฉ๋ก์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
+
+| ํํฐ๋ง | ์ข์์ ์ทจ์ |
+| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
+|
|
|
+
+#### [ํํฐ๋ง]
+
+- ํ๋ ์ด๋ฆฌ์คํธ๋ฅผ ์ต์ ์, ์ข์์์, ๋๊ธ์์ผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
+
+#### [์ข์์ ์ทจ์]
+
+- ํํธ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์ข์์๋ฅผ ์ทจ์ํ ์ ์์ต๋๋ค.
+- ์ค์๋ก ์ข์์๋ฅผ ์ทจ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋ ์์๋ง ์ข์์ ์ญ์ ๊ฐ ๋ฐ์๋๋๋ก ๊ตฌํํ์ต๋๋ค.
+
+### ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ ํ์ด์ง
+
+- ๋ง์ดํ๋ฆฌ ํ์ด์ง์์ โํ๋ ์ด๋ฆฌ์คํธ ์์ฑโ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์์ฑ ํ์ด์ง๋ก ์ง์
ํฉ๋๋ค.
+- ๊ณต๊ฐ์ฌ๋ถ ์ค์ : ๋ํดํธ๋ ๊ณต๊ฐ์ด๋ฉฐ, ๋น๊ณต๊ฐ๋ก ์ค์ ์ ๋ค๋ฅธ ์ฌ๋์๊ฒ ๋
ธ์ถ๋์ง ์์ต๋๋ค.
+- ํ๋ ์ด๋ฆฌ์คํธ ์ ๋ชฉ, ์ค๋ช
: ํ๋ ์ด๋ฆฌ์คํธ ์ ๋ชฉ๊ณผ ์ค๋ช
์ ์์ฑํฉ๋๋ค. ์ ๋ชฉ์ ํ์์ฌํญ์ด๊ณ , ์ค๋ช
์ ์ ํ์ฌํญ์
๋๋ค.
+- ์์ ๋งํฌ ์ถ๊ฐ: ์ ํ๋ธ, ๋น๋ฉ์ค, ์ฌ์ด๋ ํด๋ผ์ฐ๋์ ์์ ๋งํฌ๋ฅผ ๋ณต์ฌ, ๋ถ์ฌ๋ฃ๊ธฐ ํ ํ + ๋ฒํผ์ ๋๋ฌ ์ถ๊ฐํฉ๋๋ค. X ๋ฒํผ์ ๋๋ฌ ๋ฑ๋ก์ ์ทจ์ํ ์ ์์ต๋๋ค.
+- ํด์ํ๊ทธ ์
๋ ฅ: ํด์ํ๊ทธ๋ ์ต๋ 5๊ฐ๊น์ง ์
๋ ฅ ๊ฐ๋ฅํฉ๋๋ค.
+- ์ธ๋ค์ผ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ: ์ธ๋ค์ผ ์ด๋ฏธ์ง๋ฅผ ๋ฑ๋กํ์ง ์๋ ๊ฒฝ์ฐ, ์ฒซ๋ฒ์งธ ์์์ ์ธ๋ค์ผ์ผ๋ก ๋ณด์ฌ์ง๋๋ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ํตํด ์
๋ก๋ ์ ์ธ๋ค์ผ์ ํ์ธํ ์ ์์ต๋๋ค.
+- ๋ชจ๋ ์ ๋ณด๋ฅผ ์
๋ ฅํ ํ โํ๋ ์ด๋ฆฌ์คํธ ์์ฑโ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์์์ด ๋ฑ๋ก๋ฉ๋๋ค.
+
+ | ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ |
+ | --------------------------------------------------------------------------------------------------------------- |
+ |
|
+
+### ํ๋ก์ ํ์ด์ง
+
+#### ๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก
+
+- ๋๋ฅผ ํ๋ก์ฐํ ๋ชจ๋ ์ฑ๋ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ต๋๋ค.
+- ์ฌ๋ ๋ชจ์ ์์ด์ฝ์ ํด๋ฆญํ์ฌ ๋๋ฅผ ํ๋ก์ฐํ๊ณ ์๋ ์ฌ๋์ ์ญ์ ํ ์ ์์ต๋๋ค.
+
+| ๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก ํ์ธ ๋ฐ ํ๋ก์ ์ญ์ |
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+#### ๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก
+
+- ๋ด๊ฐ ํ๋ก์ฐํ ๋ชจ๋ ์ฑ๋ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ต๋๋ค.
+- ์ฌ๋ ๋ชจ์ ์์ด์ฝ์ ํด๋ฆญํ์ฌ ํ๋ก์์ ์ทจ์ํ ์ ์์ต๋๋ค.
+
+| ๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก ํ์ธ ๋ฐ ์ธํ๋ก์ฐ |
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+### ๊ฒ์ ๊ฒฐ๊ณผ ํ์ด์ง
+
+- ์ฌ์ฉ์๋ ํค๋ ๋ถ๋ถ์ ๊ฒ์ ์
๋ ฅ์ฐฝ์ ํตํด ๊ฒ์์ด ๊ฐ๋ฅํฉ๋๋ค.
+- ๊ฒ์์ ํ๋ ์ด๋ฆฌ์คํธ ์ ๋ชฉ๊ณผ ํด์ํ๊ทธ ๊ฒ์์ด ๊ฐ๋ฅํฉ๋๋ค.
+- ํ๋ ์ด๋ฆฌ์คํธ ์ข์์๊ฐ ๊ฐ๋ฅํ๋ฉฐ ํ๋ ์ด๋ฆฌ์คํ ๋๋ฅผ ์ ํ๋ ์ด๋ฆฌ์คํธ ์์ธํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
+
+| ๊ฒ์ ๊ฒฐ๊ณผ ํ์ด์ง |
+| --------------------------------------------------------------------------------------------------------------- |
+|
|
+
+### ๋ง์ดํ์ด์ง
+
+- ์ฌ์ฉ์๋ ์ฐ์ธก ์๋จ์ ํค๋ ๋ถ๋ถ์์ ๋ง์ดํ์ด์ง๋ก ์ด๋ํ ์ ์์ต๋๋ค.
+- ์ฌ์ฉ์๋ ํ๋กํ ํ์ด์ง์์ ๋ก๊ทธ์์ ๋ฒํผ์ ํตํด ๋ก๊ทธ์์์ด ๊ฐ๋ฅํฉ๋๋ค.
+- ๋ด๊ฐ ์ด ๋๊ธ ๋ชฉ๋ก์ ํ์ธํ ์ ์์ผ๋ฉฐ ์ ์ฒด ์ญ์ ๋๋ ๊ฐ๋ณ ์ญ์ ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
+- ๋๋ณด๊ธฐ๋ฒํผ์ ๋๋ฌ ๋๊ธ ๋๋ณด๊ธฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
+- ์ฌ์ฉ์๋ ํ๋กํ ์์ ๋ฒํผ์ ๋๋ฌ ํ๋กํ ์์ ํ์ด์ง๋ก ์ด๋ํ ์ ์์ต๋๋ค.
+
+| ๋๊ธ ์ญ์ | ๋ก๊ทธ์์ |
+| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
+|
|
|
+
+### ํ๋กํ ์์ ํ์ด์ง
+
+- ์ฌ์ฉ์๋ ๋ง์ดํ์ด์ง์์ ํ๋กํ ์์ ๋ฒํผ์ ๋๋ฌ ํ๋กํ ์์ ํ์ด์ง๋ก ์ด๋ํ ์ ์์ต๋๋ค.
+- ์ฌ์ฉ์๋ ํ๋กํ ์์ ํ์ด์ง์์ ํ๋กํ ์ด๋ฏธ์ง, ์ฑ๋์ด๋ฆ, ํด์ํ๊ทธ๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
+- ํ๋กํ ์์ ์ค ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅผ ์ ํ๋กํ ์์ ์ทจ์ ๋ชจ๋ฌ์ฐฝ์ด ๋น๋๋ค.
+
+ | ํ๋กํ ์์ ํ์ด์ง |
+ | --------------------------------------------------------------------------------------------------------------- |
+ |
|
+
+
+
+## 8. ์ค์น ๋ฐ ์คํ
-```js
-export default tseslint.config({
- languageOptions: {
- // other options...
- parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
- tsconfigRootDir: import.meta.dirname,
- },
- },
-})
```
+npm install
+```
+
+### ํด๋ผ์ด์ธํธ ์คํ
-- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
-- Optionally add `...tseslint.configs.stylisticTypeChecked`
-- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
-
-```js
-// eslint.config.js
-import react from 'eslint-plugin-react'
-
-export default tseslint.config({
- // Set the react version
- settings: { react: { version: '18.3' } },
- plugins: {
- // Add the react plugin
- react,
- },
- rules: {
- // other rules...
- // Enable its recommended rules
- ...react.configs.recommended.rules,
- ...react.configs['jsx-runtime'].rules,
- },
-})
+```
+npm run dev
```
diff --git a/eslint.config.js b/eslint.config.js
deleted file mode 100644
index 092408a..0000000
--- a/eslint.config.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import tseslint from 'typescript-eslint'
-
-export default tseslint.config(
- { ignores: ['dist'] },
- {
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
- files: ['**/*.{ts,tsx}'],
- languageOptions: {
- ecmaVersion: 2020,
- globals: globals.browser,
- },
- plugins: {
- 'react-hooks': reactHooks,
- 'react-refresh': reactRefresh,
- },
- rules: {
- ...reactHooks.configs.recommended.rules,
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
- },
- },
-)
diff --git a/gitmessage.txt b/gitmessage.txt
new file mode 100644
index 0000000..f76c7d3
--- /dev/null
+++ b/gitmessage.txt
@@ -0,0 +1,25 @@
+################
+
+# ๐ keyword: ์ค๋ช
#์ด์๋ฒํธ
+# ์) feature: ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ #35
+
+################
+
+# Commit Keyword
+
+################
+
+# feature : ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
+# fix : ๋ฒ๊ทธ ์์
+# docs : ๋ฌธ์ ์์
+# settings : ํ๋ก์ ํธ ์ธํ
๊ด๋ จ
+# style : ์ฝ๋ formatting, ์ธ๋ฏธ์ฝ๋ก ๋๋ฝ ๋ฑ ์ฝ๋ ์์ฒด์ ๋ณ๊ฒฝ์ด ์๋ ๊ฒฝ์ฐ
+# refactor : ์ฝ๋ ๋ฆฌํฉํ ๋ง
+# test : ํ
์คํธ ์ฝ๋, ๋ฆฌํฉํ ๋ง ํ
์คํธ ์ฝ๋ ์ถ๊ฐ
+# chore : ํจํค์ง ๋งค๋์ ์์ , ๊ทธ ์ธ ๊ธฐํ ์์ ex) .gitignore
+# design : CSS ๋ฑ ์ฌ์ฉ์ UI ๋์์ธ ๋ณ๊ฒฝ
+# comment : ํ์ํ ์ฃผ์ ์ถ๊ฐ ๋ฐ ๋ณ๊ฒฝ
+# rename : ํ์ผ ๋๋ ํด๋ ๋ช
์ ์์ ํ๊ฑฐ๋ ์ฎ๊ธฐ๋ ์์
๋ง ํ ๊ฒฝ์ฐ
+# remove : ํ์ผ์ ์ญ์ ํ๋ ์์
๋ง ํ ๊ฒฝ์ฐ
+# !BREAKING CHANGE : ์ปค๋ค๋ API ๋ณ๊ฒฝ์ฌํญ์ด ์๋ ๊ฒฝ์ฐ
+# !HOTFIX : ์น๋ช
์ ์ธ ๋ฒ๊ทธ๋ฅผ ๊ธํ๊ฒ ๊ณ ์ณ์ผ ํ๋ ๊ฒฝ์ฐ
\ No newline at end of file
diff --git a/index.html b/index.html
index e4b78ea..6c765a3 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,9 @@
-
+
- Vite + React + TS
+ ์ํ๋ฆฌ
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..7cfcd44
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,12789 @@
+{
+ "name": "toy-project-3-team5",
+ "version": "0.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "toy-project-3-team5",
+ "version": "0.0.0",
+ "dependencies": {
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@headlessui/react": "^2.1.3",
+ "@tanstack/react-query": "^5.53.2",
+ "axios": "^1.7.7",
+ "firebase": "^10.13.1",
+ "lucide-react": "^0.435.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-intersection-observer": "^9.13.0",
+ "react-router-dom": "^6.26.1",
+ "zustand": "^4.5.5"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.9.0",
+ "@playwright/test": "^1.46.1",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "eslint": "^8.57.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-import-order": "^2.1.4",
+ "eslint-plugin-jsx-a11y": "^6.9.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-react": "^7.35.0",
+ "eslint-plugin-react-hooks": "^4.6.2",
+ "eslint-plugin-react-refresh": "^0.4.9",
+ "globals": "^15.9.0",
+ "husky": "^8.0.0",
+ "lint-staged": "^15.2.9",
+ "prettier": "^3.3.3",
+ "typescript": "^5.5.3",
+ "typescript-eslint": "^8.0.1",
+ "vite": "^5.4.1",
+ "vite-tsconfig-paths": "^5.0.1"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
+ "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
+ "dependencies": {
+ "@babel/highlight": "^7.24.7",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
+ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
+ "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.0",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-module-transforms": "^7.25.2",
+ "@babel/helpers": "^7.25.0",
+ "@babel/parser": "^7.25.0",
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.2",
+ "@babel/types": "^7.25.2",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.25.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz",
+ "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==",
+ "dependencies": {
+ "@babel/types": "^7.25.4",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
+ "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.25.2",
+ "@babel/helper-validator-option": "^7.24.8",
+ "browserslist": "^4.23.1",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
+ "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
+ "dependencies": {
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz",
+ "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-simple-access": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/traverse": "^7.25.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
+ "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
+ "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
+ "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
+ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
+ "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz",
+ "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
+ "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz",
+ "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==",
+ "dependencies": {
+ "@babel/types": "^7.25.4"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz",
+ "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz",
+ "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz",
+ "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
+ "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
+ "dependencies": {
+ "@babel/code-frame": "^7.24.7",
+ "@babel/parser": "^7.25.0",
+ "@babel/types": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz",
+ "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.4",
+ "@babel/parser": "^7.25.4",
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.4",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz",
+ "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.24.8",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
+ "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.2.0",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.13.1",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
+ "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz",
+ "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.13.3",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz",
+ "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/cache": "^11.13.0",
+ "@emotion/serialize": "^1.3.1",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz",
+ "integrity": "sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.0",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.13.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
+ "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.0",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz",
+ "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz",
+ "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ=="
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "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/eslintrc/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@firebase/analytics": {
+ "version": "0.10.7",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.7.tgz",
+ "integrity": "sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/analytics-compat": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz",
+ "integrity": "sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==",
+ "dependencies": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-types": "0.8.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/analytics-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz",
+ "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw=="
+ },
+ "node_modules/@firebase/app": {
+ "version": "0.10.10",
+ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz",
+ "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/app-check": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.7.tgz",
+ "integrity": "sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-compat": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz",
+ "integrity": "sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==",
+ "dependencies": {
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-types": "0.5.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-interop-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
+ "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ=="
+ },
+ "node_modules/@firebase/app-check-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz",
+ "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA=="
+ },
+ "node_modules/@firebase/app-compat": {
+ "version": "0.2.40",
+ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.40.tgz",
+ "integrity": "sha512-2L5MW4MH2ya7Wvw0hzWy3ZWeB4SqC5gYXDAV5AS1lBTL4zL3k8dsqJmry/cFV00GgkCI01WJbcXvFMCXJvgyow==",
+ "dependencies": {
+ "@firebase/app": "0.10.10",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/app-types": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz",
+ "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ=="
+ },
+ "node_modules/@firebase/auth": {
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.8.tgz",
+ "integrity": "sha512-1KJlDrTrEEFTIBj9MxjAWjQ4skecBD4bmoayQ0l14QDbNc1a8qGbi+MFSJkH7O6VnGE6bTMcWSw6RrQNecqKaw==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "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.5.13",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.13.tgz",
+ "integrity": "sha512-rV6TMxUU6wBBZ2ouDMtjJsJXeewtvYvVzslzt3/P7O/kxiWlreHT/2M/1guMiXKo3zk52XK3GqP0uM2bN7fEow==",
+ "dependencies": {
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-types": "0.12.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/auth-interop-types": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz",
+ "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ=="
+ },
+ "node_modules/@firebase/auth-types": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz",
+ "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/component": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz",
+ "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==",
+ "dependencies": {
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.7.tgz",
+ "integrity": "sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-compat": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.7.tgz",
+ "integrity": "sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-types": "1.0.4",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-types": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.4.tgz",
+ "integrity": "sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==",
+ "dependencies": {
+ "@firebase/app-types": "0.9.2",
+ "@firebase/util": "1.9.7"
+ }
+ },
+ "node_modules/@firebase/firestore": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.1.tgz",
+ "integrity": "sha512-WliQNa8GVcH6EWkH0NAf+uAnxNiBuH+G8Buzr2ZS1NznOhJDK/q6Hsjv5TzNrijLTAdEfj/wk9VEv994KDSjxg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "@firebase/webchannel-wrapper": "1.0.1",
+ "@grpc/grpc-js": "~1.9.0",
+ "@grpc/proto-loader": "^0.7.8",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-compat": {
+ "version": "0.3.36",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.36.tgz",
+ "integrity": "sha512-NtoIm7CT9f+SFB7cPMCtyCSxZReh/+SII5X4TQH394S3dwhru9HIfvEOKAMuAnXsSsLH72jXPUgdsEAUqg6Oug==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-types": "3.0.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz",
+ "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/functions": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.7.tgz",
+ "integrity": "sha512-xaUsGI2kYrI8zJXgrNB7SrJKB8v1vJqR16YYi6g6dFTgBz4+VzWJFqqVU60BbdAWm6fXnUrg9gjlJQeqomT2Vg==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-compat": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.13.tgz",
+ "integrity": "sha512-qcZvJO2ed6PAD+18DanVztw7WyQVQK43HoRhxusHAwDFvK/xY+mcGpj+IpfdxTNMBGCOIxKFp4Xqk/c2nubBlQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-types": "0.6.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-types": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz",
+ "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w=="
+ },
+ "node_modules/@firebase/installations": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz",
+ "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.8.tgz",
+ "integrity": "sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-types": "0.5.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz",
+ "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x"
+ }
+ },
+ "node_modules/@firebase/logger": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz",
+ "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/messaging": {
+ "version": "0.12.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.10.tgz",
+ "integrity": "sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-compat": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz",
+ "integrity": "sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-interop-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz",
+ "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA=="
+ },
+ "node_modules/@firebase/performance": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz",
+ "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.8.tgz",
+ "integrity": "sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz",
+ "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA=="
+ },
+ "node_modules/@firebase/remote-config": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.8.tgz",
+ "integrity": "sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz",
+ "integrity": "sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-types": "0.3.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz",
+ "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA=="
+ },
+ "node_modules/@firebase/storage": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.1.tgz",
+ "integrity": "sha512-L6AJ5tWgHSi2g/gbc/2Pbm3qxmoEg9THmPIOpRsLwuz9LPeWbhyMQeGlqxWqtZGQO/z/LMjGYadNlupQj0HNfw==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-compat": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.11.tgz",
+ "integrity": "sha512-EEa9jgm/aRVIGSD0ByYAsZ0tvEKfVwSp9uFoa/97BISGWGjSNPIWjenaDvpDZ7aL8OxaGIpwuk700aHy7/T0Ug==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-types": "0.8.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz",
+ "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/util": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz",
+ "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/vertexai-preview": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz",
+ "integrity": "sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x",
+ "@firebase/app-types": "0.x"
+ }
+ },
+ "node_modules/@firebase/webchannel-wrapper": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz",
+ "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ=="
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
+ "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.7"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz",
+ "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==",
+ "dependencies": {
+ "@floating-ui/core": "^1.6.0",
+ "@floating-ui/utils": "^0.2.7"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.26.23",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz",
+ "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.1",
+ "@floating-ui/utils": "^0.2.7",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
+ "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz",
+ "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA=="
+ },
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.9.15",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
+ "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
+ "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.13",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
+ "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@headlessui/react": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.3.tgz",
+ "integrity": "sha512-Nt+NlnQbVvMHVZ/QsST6DNyfG8VWqjOYY3eZpp0PrRKpmZw+pzhwQ1F6wtNaW4jnudeC2a5MJC70vbGVcETNIg==",
+ "dependencies": {
+ "@floating-ui/react": "^0.26.16",
+ "@react-aria/focus": "^3.17.1",
+ "@react-aria/interactions": "^3.21.3",
+ "@tanstack/react-virtual": "^3.8.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^18",
+ "react-dom": "^18"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.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.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "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/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz",
+ "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==",
+ "dev": true,
+ "dependencies": {
+ "playwright": "1.46.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/@react-aria/focus": {
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.2.tgz",
+ "integrity": "sha512-Jc/IY+StjA3uqN73o6txKQ527RFU7gnG5crEl5Xy3V+gbYp2O5L3ezAo/E0Ipi2cyMbG6T5Iit1IDs7hcGu8aw==",
+ "dependencies": {
+ "@react-aria/interactions": "^3.22.2",
+ "@react-aria/utils": "^3.25.2",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/interactions": {
+ "version": "3.22.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.2.tgz",
+ "integrity": "sha512-xE/77fRVSlqHp2sfkrMeNLrqf2amF/RyuAS6T5oDJemRSgYM3UoxTbWjucPhfnoW7r32pFPHHgz4lbdX8xqD/g==",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.5",
+ "@react-aria/utils": "^3.25.2",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.5.tgz",
+ "integrity": "sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.25.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.25.2.tgz",
+ "integrity": "sha512-GdIvG8GBJJZygB4L2QJP1Gabyn2mjFsha73I2wSe+o4DYeGWoJiMZRM06PyTIxLH4S7Sn7eVDtsSBfkc2VY/NA==",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.5",
+ "@react-stately/utils": "^3.10.3",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.3",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.3.tgz",
+ "integrity": "sha512-moClv7MlVSHpbYtQIkm0Cx+on8Pgt1XqtPx6fy9rQFb2DNc9u1G3AUVnqA17buOkH1vLxAtX4MedlxMWyRCYYA==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.24.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.24.1.tgz",
+ "integrity": "sha512-AUQeGYEm/zDTN6zLzdXolDxz3Jk5dDL7f506F07U8tBwxNNI3WRdhU84G0/AaFikOZzDXhOZDr3MhQMzyE7Ydw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
+ "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz",
+ "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz",
+ "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz",
+ "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz",
+ "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz",
+ "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz",
+ "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz",
+ "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz",
+ "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz",
+ "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz",
+ "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz",
+ "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz",
+ "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz",
+ "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz",
+ "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz",
+ "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz",
+ "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.12",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz",
+ "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.53.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.3.tgz",
+ "integrity": "sha512-ZfjAgd7NpqDx0e4aYBt7EmS2enbulPrJwowTy+mayRE93WUUH+sIYHun1TdRjpGwDPMNNZ5D6goh7n3CwoO+HA==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.53.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.53.3.tgz",
+ "integrity": "sha512-286mN/91CeM7vC6CZFLKYDHSw+WyMX6ekIvzoTbpM4xyPb99VSyCKPLyPgaOatKqYm6ooMBquSq9NGRdKgsJfg==",
+ "dependencies": {
+ "@tanstack/query-core": "5.53.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.4.tgz",
+ "integrity": "sha512-Y2y1QJN3e5gNTG4wlZcoW2IAFrVCuho80oyeODKKFVSbAhJAXmkDNH3ZztM6EQij5ueqpqgz5FlsgKP9TGjImA==",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.10.4"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.4.tgz",
+ "integrity": "sha512-yHyli4RHVsI+eJ0RjmOsjA9RpHp3/Zah9t+iRjmFa72dq00TeG/NwuLYuCV6CB4RkWD4i5RD421j1eb6BdKgvQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
+ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "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": "22.5.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz",
+ "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
+ "devOptional": true
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.4",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
+ "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
+ "devOptional": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
+ "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/type-utils": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz",
+ "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
+ "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz",
+ "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
+ "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
+ "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
+ "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
+ "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.2.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.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/@vitejs/plugin-react": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz",
+ "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.24.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.24.5",
+ "@babel/plugin-transform-react-jsx-source": "^7.24.1",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "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-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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.1.3",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+ "dev": true,
+ "dependencies": {
+ "deep-equal": "^2.0.5"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "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.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
+ "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "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.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "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/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.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz",
+ "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
+ "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
+ "dev": true,
+ "dependencies": {
+ "deep-equal": "^2.0.5"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "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/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.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001653",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz",
+ "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==",
+ "dev": true,
+ "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": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chalk/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "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/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=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/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "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/data-view-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-equal": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.5",
+ "es-get-iterator": "^1.1.3",
+ "get-intrinsic": "^1.2.2",
+ "is-arguments": "^1.1.1",
+ "is-array-buffer": "^3.0.2",
+ "is-date-object": "^1.0.5",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "isarray": "^2.0.5",
+ "object-is": "^1.1.5",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.1",
+ "side-channel": "^1.0.4",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.1",
+ "which-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/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/electron-to-chromium": {
+ "version": "1.5.13",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
+ "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==",
+ "dev": true
+ },
+ "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/enhanced-resolve": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "data-view-buffer": "^1.0.1",
+ "data-view-byte-length": "^1.0.1",
+ "data-view-byte-offset": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.1",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.2",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.9",
+ "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.6",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-get-iterator": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "is-arguments": "^1.1.1",
+ "is-map": "^2.0.2",
+ "is-set": "^2.0.2",
+ "is-string": "^1.0.7",
+ "isarray": "^2.0.5",
+ "stop-iteration-iterator": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz",
+ "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "iterator.prototype": "^1.1.2",
+ "safe-array-concat": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ },
+ "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/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "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==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@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-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
+ "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
+ "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
+ "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.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import-order": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-order/-/eslint-plugin-import-order-2.1.4.tgz",
+ "integrity": "sha512-cHThJ45zFx5UtRZ+Jd6XO89bLriKkdckAHZoSxknlK3BELmAO6Gqe74A7j8rvL+1PEyNJF3Fsil/Rjwx8cqOow==",
+ "deprecated": "Use eslint-plugin-import instead (order rule)",
+ "dev": true,
+ "dependencies": {
+ "builtin-modules": "^1.1.1",
+ "lodash.cond": "^4.2.0",
+ "lodash.find": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=2"
+ }
+ },
+ "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-jsx-a11y": {
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz",
+ "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==",
+ "dev": true,
+ "dependencies": {
+ "aria-query": "~5.1.3",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.9.1",
+ "axobject-query": "~3.1.1",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "es-iterator-helpers": "^1.0.19",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
+ "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.35.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz",
+ "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.2",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.0.19",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.8",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.0",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.11",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "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-refresh": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz",
+ "integrity": "sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw==",
+ "dev": true,
+ "peerDependencies": {
+ "eslint": ">=7"
+ }
+ },
+ "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-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/eslint/node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/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==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/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/eslint/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/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==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/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/eslint/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/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.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "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/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "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.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "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.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
+ "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.13.1",
+ "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.1.tgz",
+ "integrity": "sha512-L5BSkmvB2dzCUMpr8i/O8WMJC3Nqj5Ld8Wj/qnak+tz2Ga+JH6/FO93xArg9IGhktCrPXVODoWp6t9ybdgmXCA==",
+ "dependencies": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-compat": "0.2.13",
+ "@firebase/app": "0.10.10",
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-compat": "0.3.14",
+ "@firebase/app-compat": "0.2.40",
+ "@firebase/app-types": "0.9.2",
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-compat": "0.5.13",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-compat": "1.0.7",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-compat": "0.3.36",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-compat": "0.3.13",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-compat": "0.2.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/messaging-compat": "0.2.10",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-compat": "0.2.8",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-compat": "0.2.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-compat": "0.3.11",
+ "@firebase/util": "1.9.7",
+ "@firebase/vertexai-preview": "0.0.3"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "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==",
+ "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/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "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-east-asian-width": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
+ "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz",
+ "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/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/globals": {
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "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/globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+ "dev": true
+ },
+ "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==",
+ "dev": true
+ },
+ "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/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": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "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": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+ "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+ "dev": true,
+ "bin": {
+ "husky": "lib/bin.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "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.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "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==",
+ "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==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "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.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "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-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "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-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.15.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dev": true,
+ "dependencies": {
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": 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": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "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==",
+ "dev": 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.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "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": "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/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/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.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "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/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": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
+ "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/lint-staged": {
+ "version": "15.2.9",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.9.tgz",
+ "integrity": "sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "~5.3.0",
+ "commander": "~12.1.0",
+ "debug": "~4.3.6",
+ "execa": "~8.0.1",
+ "lilconfig": "~3.1.2",
+ "listr2": "~8.2.4",
+ "micromatch": "~4.0.7",
+ "pidtree": "~0.6.0",
+ "string-argv": "~0.3.2",
+ "yaml": "~2.5.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/lint-staged/node_modules/yaml": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
+ "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz",
+ "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==",
+ "dev": true,
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "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.cond": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
+ "integrity": "sha512-RWjUhzGbzG/KfDwk+onqdXvrsNv47G9UCMJgSKalPTSqJQyxZhQophG9jgqLf+15TIbZ5a/yG2YKOWsH3dVy9A==",
+ "dev": true
+ },
+ "node_modules/lodash.find": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz",
+ "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==",
+ "dev": true
+ },
+ "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/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "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": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.435.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.435.0.tgz",
+ "integrity": "sha512-we5GKfzjMDw9m9SsyZJvWim9qaT+Ya5kaRS+OGFqgLqXUrPM1h+7CiMw5pKdEIoaBqfXz2pyv9TASAdpIAJs0Q==",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ }
+ },
+ "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.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "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/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "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==",
+ "dev": true,
+ "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/node-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "dev": true
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "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.8",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
+ "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
+ "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "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/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "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==",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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=="
+ },
+ "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==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz",
+ "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.46.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz",
+ "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.41",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
+ "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
+ "dev": true,
+ "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.7",
+ "picocolors": "^1.0.1",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "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.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "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.4.0",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+ "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "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/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.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-intersection-observer": {
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.13.0.tgz",
+ "integrity": "sha512-y0UvBfjDiXqC8h0EWccyaj4dVBWMxgEx0t5RGNzQsvkfvZwugnKwxpu70StY4ivzYuMajavwUDjH4LJyIki9Lw==",
+ "peerDependencies": {
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "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/react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
+ "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.19.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
+ "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.19.1",
+ "react-router": "6.26.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
+ "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.1",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ },
+ "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/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "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==",
+ "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/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz",
+ "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.21.0",
+ "@rollup/rollup-android-arm64": "4.21.0",
+ "@rollup/rollup-darwin-arm64": "4.21.0",
+ "@rollup/rollup-darwin-x64": "4.21.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.21.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.21.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.21.0",
+ "@rollup/rollup-linux-arm64-musl": "4.21.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.21.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.21.0",
+ "@rollup/rollup-linux-x64-gnu": "4.21.0",
+ "@rollup/rollup-linux-x64-musl": "4.21.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.21.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.21.0",
+ "@rollup/rollup-win32-x64-msvc": "4.21.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "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.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "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.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "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/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "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.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+ "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+ "dev": true,
+ "dependencies": {
+ "internal-slot": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz",
+ "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
+ "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "regexp.prototype.flags": "^1.5.2",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
+ "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+ },
+ "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/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/tsconfck": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz",
+ "integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==",
+ "dev": true,
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/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/tslib": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
+ },
+ "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.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.2.0.tgz",
+ "integrity": "sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.2.0",
+ "@typescript-eslint/parser": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "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": {
+ "version": "6.19.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz",
+ "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "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/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz",
+ "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.41",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-tsconfig-paths": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.0.1.tgz",
+ "integrity": "sha512-yqwv+LstU7NwPeNqajZzLEBVpUFU6Dugtb2P84FXuvaoYA+/70l9MHE+GYfYAycVyPSDYZ7mjOFuYBRqlEpTig==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^3.0.3"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/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/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/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.4",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz",
+ "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==",
+ "dev": true,
+ "dependencies": {
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "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.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-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/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": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "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==",
+ "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/yargs/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/yargs/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/yargs/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/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"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz",
+ "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==",
+ "dependencies": {
+ "use-sync-external-store": "1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
+ "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
+ "requires": {
+ "@babel/highlight": "^7.24.7",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
+ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
+ "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.0",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-module-transforms": "^7.25.2",
+ "@babel/helpers": "^7.25.0",
+ "@babel/parser": "^7.25.0",
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.2",
+ "@babel/types": "^7.25.2",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "dependencies": {
+ "convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.25.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz",
+ "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==",
+ "requires": {
+ "@babel/types": "^7.25.4",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
+ "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.25.2",
+ "@babel/helper-validator-option": "^7.24.8",
+ "browserslist": "^4.23.1",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
+ "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
+ "requires": {
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz",
+ "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-simple-access": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/traverse": "^7.25.2"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
+ "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==",
+ "dev": true
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
+ "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
+ "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ=="
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
+ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w=="
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
+ "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz",
+ "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
+ "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz",
+ "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==",
+ "requires": {
+ "@babel/types": "^7.25.4"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-self": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz",
+ "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-source": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz",
+ "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz",
+ "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==",
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@babel/template": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
+ "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
+ "requires": {
+ "@babel/code-frame": "^7.24.7",
+ "@babel/parser": "^7.25.0",
+ "@babel/types": "^7.25.0"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz",
+ "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==",
+ "requires": {
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.4",
+ "@babel/parser": "^7.25.4",
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.4",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz",
+ "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.24.8",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@emotion/babel-plugin": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
+ "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.2.0",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/cache": {
+ "version": "11.13.1",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
+ "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
+ "requires": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ },
+ "@emotion/is-prop-valid": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz",
+ "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==",
+ "requires": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "@emotion/react": {
+ "version": "11.13.3",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz",
+ "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/cache": "^11.13.0",
+ "@emotion/serialize": "^1.3.1",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ }
+ },
+ "@emotion/serialize": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz",
+ "integrity": "sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==",
+ "requires": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.0",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ },
+ "@emotion/styled": {
+ "version": "11.13.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
+ "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.0",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0"
+ }
+ },
+ "@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz",
+ "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==",
+ "requires": {}
+ },
+ "@emotion/utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz",
+ "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ },
+ "@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "dev": true,
+ "optional": true
+ },
+ "@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,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true
+ },
+ "@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "requires": {
+ "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"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ }
+ }
+ },
+ "@eslint/js": {
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
+ "dev": true
+ },
+ "@firebase/analytics": {
+ "version": "0.10.7",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.7.tgz",
+ "integrity": "sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/analytics-compat": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz",
+ "integrity": "sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==",
+ "requires": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-types": "0.8.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/analytics-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz",
+ "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw=="
+ },
+ "@firebase/app": {
+ "version": "0.10.10",
+ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz",
+ "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/app-check": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.7.tgz",
+ "integrity": "sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/app-check-compat": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz",
+ "integrity": "sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==",
+ "requires": {
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-types": "0.5.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/app-check-interop-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
+ "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ=="
+ },
+ "@firebase/app-check-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz",
+ "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA=="
+ },
+ "@firebase/app-compat": {
+ "version": "0.2.40",
+ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.40.tgz",
+ "integrity": "sha512-2L5MW4MH2ya7Wvw0hzWy3ZWeB4SqC5gYXDAV5AS1lBTL4zL3k8dsqJmry/cFV00GgkCI01WJbcXvFMCXJvgyow==",
+ "requires": {
+ "@firebase/app": "0.10.10",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/app-types": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz",
+ "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ=="
+ },
+ "@firebase/auth": {
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.8.tgz",
+ "integrity": "sha512-1KJlDrTrEEFTIBj9MxjAWjQ4skecBD4bmoayQ0l14QDbNc1a8qGbi+MFSJkH7O6VnGE6bTMcWSw6RrQNecqKaw==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ }
+ },
+ "@firebase/auth-compat": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.13.tgz",
+ "integrity": "sha512-rV6TMxUU6wBBZ2ouDMtjJsJXeewtvYvVzslzt3/P7O/kxiWlreHT/2M/1guMiXKo3zk52XK3GqP0uM2bN7fEow==",
+ "requires": {
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-types": "0.12.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ }
+ },
+ "@firebase/auth-interop-types": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz",
+ "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ=="
+ },
+ "@firebase/auth-types": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz",
+ "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==",
+ "requires": {}
+ },
+ "@firebase/component": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz",
+ "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==",
+ "requires": {
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.7.tgz",
+ "integrity": "sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==",
+ "requires": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database-compat": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.7.tgz",
+ "integrity": "sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-types": "1.0.4",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database-types": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.4.tgz",
+ "integrity": "sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==",
+ "requires": {
+ "@firebase/app-types": "0.9.2",
+ "@firebase/util": "1.9.7"
+ }
+ },
+ "@firebase/firestore": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.1.tgz",
+ "integrity": "sha512-WliQNa8GVcH6EWkH0NAf+uAnxNiBuH+G8Buzr2ZS1NznOhJDK/q6Hsjv5TzNrijLTAdEfj/wk9VEv994KDSjxg==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "@firebase/webchannel-wrapper": "1.0.1",
+ "@grpc/grpc-js": "~1.9.0",
+ "@grpc/proto-loader": "^0.7.8",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ }
+ },
+ "@firebase/firestore-compat": {
+ "version": "0.3.36",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.36.tgz",
+ "integrity": "sha512-NtoIm7CT9f+SFB7cPMCtyCSxZReh/+SII5X4TQH394S3dwhru9HIfvEOKAMuAnXsSsLH72jXPUgdsEAUqg6Oug==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-types": "3.0.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/firestore-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz",
+ "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==",
+ "requires": {}
+ },
+ "@firebase/functions": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.7.tgz",
+ "integrity": "sha512-xaUsGI2kYrI8zJXgrNB7SrJKB8v1vJqR16YYi6g6dFTgBz4+VzWJFqqVU60BbdAWm6fXnUrg9gjlJQeqomT2Vg==",
+ "requires": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ }
+ },
+ "@firebase/functions-compat": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.13.tgz",
+ "integrity": "sha512-qcZvJO2ed6PAD+18DanVztw7WyQVQK43HoRhxusHAwDFvK/xY+mcGpj+IpfdxTNMBGCOIxKFp4Xqk/c2nubBlQ==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-types": "0.6.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/functions-types": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz",
+ "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w=="
+ },
+ "@firebase/installations": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz",
+ "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/installations-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.8.tgz",
+ "integrity": "sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-types": "0.5.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/installations-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz",
+ "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==",
+ "requires": {}
+ },
+ "@firebase/logger": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz",
+ "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/messaging": {
+ "version": "0.12.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.10.tgz",
+ "integrity": "sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/messaging-compat": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz",
+ "integrity": "sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/messaging-interop-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz",
+ "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA=="
+ },
+ "@firebase/performance": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz",
+ "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/performance-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.8.tgz",
+ "integrity": "sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/performance-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz",
+ "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA=="
+ },
+ "@firebase/remote-config": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.8.tgz",
+ "integrity": "sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/remote-config-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz",
+ "integrity": "sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-types": "0.3.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/remote-config-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz",
+ "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA=="
+ },
+ "@firebase/storage": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.1.tgz",
+ "integrity": "sha512-L6AJ5tWgHSi2g/gbc/2Pbm3qxmoEg9THmPIOpRsLwuz9LPeWbhyMQeGlqxWqtZGQO/z/LMjGYadNlupQj0HNfw==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ }
+ },
+ "@firebase/storage-compat": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.11.tgz",
+ "integrity": "sha512-EEa9jgm/aRVIGSD0ByYAsZ0tvEKfVwSp9uFoa/97BISGWGjSNPIWjenaDvpDZ7aL8OxaGIpwuk700aHy7/T0Ug==",
+ "requires": {
+ "@firebase/component": "0.6.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-types": "0.8.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/storage-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz",
+ "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==",
+ "requires": {}
+ },
+ "@firebase/util": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz",
+ "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/vertexai-preview": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz",
+ "integrity": "sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==",
+ "requires": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/webchannel-wrapper": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz",
+ "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ=="
+ },
+ "@floating-ui/core": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
+ "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==",
+ "requires": {
+ "@floating-ui/utils": "^0.2.7"
+ }
+ },
+ "@floating-ui/dom": {
+ "version": "1.6.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz",
+ "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==",
+ "requires": {
+ "@floating-ui/core": "^1.6.0",
+ "@floating-ui/utils": "^0.2.7"
+ }
+ },
+ "@floating-ui/react": {
+ "version": "0.26.23",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz",
+ "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==",
+ "requires": {
+ "@floating-ui/react-dom": "^2.1.1",
+ "@floating-ui/utils": "^0.2.7",
+ "tabbable": "^6.0.0"
+ }
+ },
+ "@floating-ui/react-dom": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
+ "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
+ "requires": {
+ "@floating-ui/dom": "^1.0.0"
+ }
+ },
+ "@floating-ui/utils": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz",
+ "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA=="
+ },
+ "@grpc/grpc-js": {
+ "version": "1.9.15",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
+ "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
+ "requires": {
+ "@grpc/proto-loader": "^0.7.8",
+ "@types/node": ">=12.12.47"
+ }
+ },
+ "@grpc/proto-loader": {
+ "version": "0.7.13",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
+ "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ }
+ },
+ "@headlessui/react": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.3.tgz",
+ "integrity": "sha512-Nt+NlnQbVvMHVZ/QsST6DNyfG8VWqjOYY3eZpp0PrRKpmZw+pzhwQ1F6wtNaW4jnudeC2a5MJC70vbGVcETNIg==",
+ "requires": {
+ "@floating-ui/react": "^0.26.16",
+ "@react-aria/focus": "^3.17.1",
+ "@react-aria/interactions": "^3.21.3",
+ "@tanstack/react-virtual": "^3.8.1"
+ }
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ }
+ },
+ "@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
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "requires": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
+ },
+ "@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "@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,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@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
+ },
+ "@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,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true
+ },
+ "@playwright/test": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz",
+ "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==",
+ "dev": true,
+ "requires": {
+ "playwright": "1.46.1"
+ }
+ },
+ "@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=="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "@react-aria/focus": {
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.2.tgz",
+ "integrity": "sha512-Jc/IY+StjA3uqN73o6txKQ527RFU7gnG5crEl5Xy3V+gbYp2O5L3ezAo/E0Ipi2cyMbG6T5Iit1IDs7hcGu8aw==",
+ "requires": {
+ "@react-aria/interactions": "^3.22.2",
+ "@react-aria/utils": "^3.25.2",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ }
+ },
+ "@react-aria/interactions": {
+ "version": "3.22.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.2.tgz",
+ "integrity": "sha512-xE/77fRVSlqHp2sfkrMeNLrqf2amF/RyuAS6T5oDJemRSgYM3UoxTbWjucPhfnoW7r32pFPHHgz4lbdX8xqD/g==",
+ "requires": {
+ "@react-aria/ssr": "^3.9.5",
+ "@react-aria/utils": "^3.25.2",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "@react-aria/ssr": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.5.tgz",
+ "integrity": "sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==",
+ "requires": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "@react-aria/utils": {
+ "version": "3.25.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.25.2.tgz",
+ "integrity": "sha512-GdIvG8GBJJZygB4L2QJP1Gabyn2mjFsha73I2wSe+o4DYeGWoJiMZRM06PyTIxLH4S7Sn7eVDtsSBfkc2VY/NA==",
+ "requires": {
+ "@react-aria/ssr": "^3.9.5",
+ "@react-stately/utils": "^3.10.3",
+ "@react-types/shared": "^3.24.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ }
+ },
+ "@react-stately/utils": {
+ "version": "3.10.3",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.3.tgz",
+ "integrity": "sha512-moClv7MlVSHpbYtQIkm0Cx+on8Pgt1XqtPx6fy9rQFb2DNc9u1G3AUVnqA17buOkH1vLxAtX4MedlxMWyRCYYA==",
+ "requires": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "@react-types/shared": {
+ "version": "3.24.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.24.1.tgz",
+ "integrity": "sha512-AUQeGYEm/zDTN6zLzdXolDxz3Jk5dDL7f506F07U8tBwxNNI3WRdhU84G0/AaFikOZzDXhOZDr3MhQMzyE7Ydw==",
+ "requires": {}
+ },
+ "@remix-run/router": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
+ "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg=="
+ },
+ "@rollup/rollup-android-arm-eabi": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz",
+ "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-android-arm64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz",
+ "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-arm64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz",
+ "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-x64": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz",
+ "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz",
+ "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz",
+ "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz",
+ "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-musl": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz",
+ "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz",
+ "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz",
+ "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz",
+ "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-gnu": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz",
+ "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-musl": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz",
+ "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz",
+ "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz",
+ "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-x64-msvc": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz",
+ "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@swc/helpers": {
+ "version": "0.5.12",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz",
+ "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==",
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "@tanstack/query-core": {
+ "version": "5.53.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.3.tgz",
+ "integrity": "sha512-ZfjAgd7NpqDx0e4aYBt7EmS2enbulPrJwowTy+mayRE93WUUH+sIYHun1TdRjpGwDPMNNZ5D6goh7n3CwoO+HA=="
+ },
+ "@tanstack/react-query": {
+ "version": "5.53.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.53.3.tgz",
+ "integrity": "sha512-286mN/91CeM7vC6CZFLKYDHSw+WyMX6ekIvzoTbpM4xyPb99VSyCKPLyPgaOatKqYm6ooMBquSq9NGRdKgsJfg==",
+ "requires": {
+ "@tanstack/query-core": "5.53.3"
+ }
+ },
+ "@tanstack/react-virtual": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.4.tgz",
+ "integrity": "sha512-Y2y1QJN3e5gNTG4wlZcoW2IAFrVCuho80oyeODKKFVSbAhJAXmkDNH3ZztM6EQij5ueqpqgz5FlsgKP9TGjImA==",
+ "requires": {
+ "@tanstack/virtual-core": "3.10.4"
+ }
+ },
+ "@tanstack/virtual-core": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.4.tgz",
+ "integrity": "sha512-yHyli4RHVsI+eJ0RjmOsjA9RpHp3/Zah9t+iRjmFa72dq00TeG/NwuLYuCV6CB4RkWD4i5RD421j1eb6BdKgvQ=="
+ },
+ "@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__traverse": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
+ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "@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
+ },
+ "@types/node": {
+ "version": "22.5.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz",
+ "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==",
+ "requires": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ },
+ "@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
+ "devOptional": true
+ },
+ "@types/react": {
+ "version": "18.3.4",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
+ "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
+ "devOptional": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
+ "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/type-utils": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz",
+ "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
+ "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0"
+ }
+ },
+ "@typescript-eslint/type-utils": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz",
+ "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
+ "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
+ "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true
+ }
+ }
+ },
+ "@typescript-eslint/utils": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
+ "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
+ "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.2.0",
+ "eslint-visitor-keys": "^3.4.3"
+ }
+ },
+ "@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
+ },
+ "@vitejs/plugin-react": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz",
+ "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.24.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.24.5",
+ "@babel/plugin-transform-react-jsx-source": "^7.24.1",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.2"
+ }
+ },
+ "acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true
+ },
+ "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,
+ "requires": {}
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "requires": {
+ "environment": "^1.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "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
+ },
+ "aria-query": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+ "dev": true,
+ "requires": {
+ "deep-equal": "^2.0.5"
+ }
+ },
+ "array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ }
+ },
+ "array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "is-string": "^1.0.7"
+ }
+ },
+ "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
+ },
+ "array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ }
+ },
+ "array.prototype.findlastindex": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
+ "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ }
+ },
+ "array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ }
+ },
+ "arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dev": true,
+ "requires": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "is-shared-array-buffer": "^1.0.2"
+ }
+ },
+ "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
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "requires": {
+ "possible-typed-array-names": "^1.0.0"
+ }
+ },
+ "axe-core": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz",
+ "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==",
+ "dev": true
+ },
+ "axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "requires": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "axobject-query": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
+ "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
+ "dev": true,
+ "requires": {
+ "deep-equal": "^2.0.5"
+ }
+ },
+ "babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.1.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
+ "dev": true
+ },
+ "call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001653",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz",
+ "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+ }
+ }
+ },
+ "cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^5.0.0"
+ }
+ },
+ "cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "requires": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ }
+ },
+ "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==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "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=="
+ },
+ "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=="
+ },
+ "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=="
+ },
+ "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==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "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==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ }
+ }
+ },
+ "clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ }
+ },
+ "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,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "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
+ },
+ "data-view-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ }
+ },
+ "data-view-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ }
+ },
+ "data-view-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ }
+ },
+ "debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-equal": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
+ "dev": true,
+ "requires": {
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.5",
+ "es-get-iterator": "^1.1.3",
+ "get-intrinsic": "^1.2.2",
+ "is-arguments": "^1.1.1",
+ "is-array-buffer": "^3.0.2",
+ "is-date-object": "^1.0.5",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "isarray": "^2.0.5",
+ "object-is": "^1.1.5",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.1",
+ "side-channel": "^1.0.4",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.1",
+ "which-typed-array": "^1.1.13"
+ }
+ },
+ "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
+ },
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
+ "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,
+ "requires": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "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,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.5.13",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
+ "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==",
+ "dev": true
+ },
+ "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
+ },
+ "enhanced-resolve": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dev": true,
+ "requires": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "data-view-buffer": "^1.0.1",
+ "data-view-byte-length": "^1.0.1",
+ "data-view-byte-offset": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.1",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.2",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.9",
+ "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.6",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ }
+ },
+ "es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.2.4"
+ }
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true
+ },
+ "es-get-iterator": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "is-arguments": "^1.1.1",
+ "is-map": "^2.0.2",
+ "is-set": "^2.0.2",
+ "is-string": "^1.0.7",
+ "isarray": "^2.0.5",
+ "stop-iteration-iterator": "^1.0.0"
+ }
+ },
+ "es-iterator-helpers": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz",
+ "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "iterator.prototype": "^1.1.2",
+ "safe-array-concat": "^1.1.2"
+ }
+ },
+ "es-object-atoms": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0"
+ }
+ },
+ "es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ }
+ },
+ "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,
+ "requires": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "requires": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA=="
+ },
+ "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=="
+ },
+ "eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@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"
+ },
+ "dependencies": {
+ "@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "requires": {}
+ },
+ "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,
+ "requires": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "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,
+ "requires": {
+ "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"
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
+ "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.7"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
+ "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
+ "dev": true,
+ "requires": {
+ "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.15.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ }
+ }
+ },
+ "eslint-plugin-import-order": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-order/-/eslint-plugin-import-order-2.1.4.tgz",
+ "integrity": "sha512-cHThJ45zFx5UtRZ+Jd6XO89bLriKkdckAHZoSxknlK3BELmAO6Gqe74A7j8rvL+1PEyNJF3Fsil/Rjwx8cqOow==",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.1.1",
+ "lodash.cond": "^4.2.0",
+ "lodash.find": "^4.2.0"
+ }
+ },
+ "eslint-plugin-jsx-a11y": {
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz",
+ "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==",
+ "dev": true,
+ "requires": {
+ "aria-query": "~5.1.3",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.9.1",
+ "axobject-query": "~3.1.1",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "es-iterator-helpers": "^1.0.19",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.0"
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
+ "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ }
+ },
+ "eslint-plugin-react": {
+ "version": "7.35.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz",
+ "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.2",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.0.19",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.8",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.0",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.11",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "eslint-plugin-react-hooks": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-plugin-react-refresh": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz",
+ "integrity": "sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw==",
+ "dev": true,
+ "requires": {}
+ },
+ "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,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ }
+ },
+ "esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "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,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "@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"
+ },
+ "dependencies": {
+ "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,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "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,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
+ "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,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "firebase": {
+ "version": "10.13.1",
+ "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.1.tgz",
+ "integrity": "sha512-L5BSkmvB2dzCUMpr8i/O8WMJC3Nqj5Ld8Wj/qnak+tz2Ga+JH6/FO93xArg9IGhktCrPXVODoWp6t9ybdgmXCA==",
+ "requires": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-compat": "0.2.13",
+ "@firebase/app": "0.10.10",
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-compat": "0.3.14",
+ "@firebase/app-compat": "0.2.40",
+ "@firebase/app-types": "0.9.2",
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-compat": "0.5.13",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-compat": "1.0.7",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-compat": "0.3.36",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-compat": "0.3.13",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-compat": "0.2.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/messaging-compat": "0.2.10",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-compat": "0.2.8",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-compat": "0.2.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-compat": "0.3.11",
+ "@firebase/util": "1.9.7",
+ "@firebase/vertexai-preview": "0.0.3"
+ }
+ },
+ "flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
+ },
+ "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,
+ "requires": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "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==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "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
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ }
+ },
+ "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
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "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=="
+ },
+ "get-east-asian-width": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
+ "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true
+ },
+ "get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ }
+ },
+ "get-tsconfig": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz",
+ "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==",
+ "dev": true,
+ "requires": {
+ "resolve-pkg-maps": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "globals": {
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
+ "dev": true
+ },
+ "globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ }
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ },
+ "globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+ "dev": true
+ },
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "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
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+ },
+ "has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0"
+ }
+ },
+ "has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true
+ },
+ "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
+ },
+ "has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
+ "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==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "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=="
+ },
+ "human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true
+ },
+ "husky": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+ "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+ "dev": true
+ },
+ "idb": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
+ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="
+ },
+ "ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "internal-slot": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "requires": {
+ "has-bigints": "^1.0.1"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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
+ },
+ "is-core-module": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "requires": {
+ "hasown": "^2.0.2"
+ }
+ },
+ "is-data-view": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dev": true,
+ "requires": {
+ "is-typed-array": "^1.1.13"
+ }
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true
+ },
+ "is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7"
+ }
+ },
+ "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
+ },
+ "is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dev": true,
+ "requires": {
+ "which-typed-array": "^1.1.14"
+ }
+ },
+ "is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-weakset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ }
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "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"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "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,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
+ },
+ "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
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "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
+ },
+ "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
+ },
+ "json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ }
+ },
+ "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,
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "language-subtag-registry": "^0.3.20"
+ }
+ },
+ "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,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "lilconfig": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
+ "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
+ "dev": true
+ },
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "lint-staged": {
+ "version": "15.2.9",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.9.tgz",
+ "integrity": "sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "~5.3.0",
+ "commander": "~12.1.0",
+ "debug": "~4.3.6",
+ "execa": "~8.0.1",
+ "lilconfig": "~3.1.2",
+ "listr2": "~8.2.4",
+ "micromatch": "~4.0.7",
+ "pidtree": "~0.6.0",
+ "string-argv": "~0.3.2",
+ "yaml": "~2.5.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
+ "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
+ "dev": true
+ }
+ }
+ },
+ "listr2": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz",
+ "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==",
+ "dev": true,
+ "requires": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "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=="
+ },
+ "lodash.cond": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
+ "integrity": "sha512-RWjUhzGbzG/KfDwk+onqdXvrsNv47G9UCMJgSKalPTSqJQyxZhQophG9jgqLf+15TIbZ5a/yG2YKOWsH3dVy9A==",
+ "dev": true
+ },
+ "lodash.find": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz",
+ "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==",
+ "dev": true
+ },
+ "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
+ },
+ "log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "requires": {
+ "get-east-asian-width": "^1.0.0"
+ }
+ },
+ "slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "long": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+ },
+ "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==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "lucide-react": {
+ "version": "0.435.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.435.0.tgz",
+ "integrity": "sha512-we5GKfzjMDw9m9SsyZJvWim9qaT+Ya5kaRS+OGFqgLqXUrPM1h+7CiMw5pKdEIoaBqfXz2pyv9TASAdpIAJs0Q==",
+ "requires": {}
+ },
+ "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
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "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==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "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
+ },
+ "mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true
+ },
+ "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-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+ "dev": true
+ },
+ "object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ }
+ },
+ "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
+ },
+ "object.assign": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
+ "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ }
+ },
+ "object.values": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
+ "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "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,
+ "requires": {
+ "mimic-fn": "^4.0.0"
+ }
+ },
+ "optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ }
+ },
+ "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,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "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,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "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==",
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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
+ },
+ "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=="
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
+ },
+ "picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true
+ },
+ "playwright": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz",
+ "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==",
+ "dev": true,
+ "requires": {
+ "fsevents": "2.3.2",
+ "playwright-core": "1.46.1"
+ }
+ },
+ "playwright-core": {
+ "version": "1.46.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz",
+ "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==",
+ "dev": true
+ },
+ "possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.41",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
+ "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.1",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "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
+ },
+ "prettier": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "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,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "protobufjs": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+ "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "requires": {
+ "@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"
+ }
+ },
+ "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=="
+ },
+ "punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true
+ },
+ "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
+ },
+ "react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ }
+ },
+ "react-intersection-observer": {
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.13.0.tgz",
+ "integrity": "sha512-y0UvBfjDiXqC8h0EWccyaj4dVBWMxgEx0t5RGNzQsvkfvZwugnKwxpu70StY4ivzYuMajavwUDjH4LJyIki9Lw==",
+ "requires": {}
+ },
+ "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=="
+ },
+ "react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "dev": true
+ },
+ "react-router": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
+ "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
+ "requires": {
+ "@remix-run/router": "1.19.1"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
+ "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
+ "requires": {
+ "@remix-run/router": "1.19.1",
+ "react-router": "6.26.1"
+ }
+ },
+ "reflect.getprototypeof": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
+ "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.1",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "which-builtin-type": "^1.1.3"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
+ "regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ }
+ },
+ "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=="
+ },
+ "resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "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=="
+ },
+ "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
+ },
+ "restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "dependencies": {
+ "onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "requires": {
+ "mimic-function": "^5.0.0"
+ }
+ }
+ }
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rollup": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz",
+ "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==",
+ "dev": true,
+ "requires": {
+ "@rollup/rollup-android-arm-eabi": "4.21.0",
+ "@rollup/rollup-android-arm64": "4.21.0",
+ "@rollup/rollup-darwin-arm64": "4.21.0",
+ "@rollup/rollup-darwin-x64": "4.21.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.21.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.21.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.21.0",
+ "@rollup/rollup-linux-arm64-musl": "4.21.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.21.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.21.0",
+ "@rollup/rollup-linux-x64-gnu": "4.21.0",
+ "@rollup/rollup-linux-x64-musl": "4.21.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.21.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.21.0",
+ "@rollup/rollup-win32-x64-msvc": "4.21.0",
+ "@types/estree": "1.0.5",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "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,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "safe-array-concat": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ }
+ },
+ "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=="
+ },
+ "safe-regex-test": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ }
+ },
+ "scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ },
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ }
+ },
+ "set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ }
+ },
+ "signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ },
+ "source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true
+ },
+ "stop-iteration-iterator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+ "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+ "dev": true,
+ "requires": {
+ "internal-slot": "^1.0.4"
+ }
+ },
+ "string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "string.prototype.includes": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz",
+ "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.matchall": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
+ "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "regexp.prototype.flags": "^1.5.2",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.0.6"
+ }
+ },
+ "string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.trim": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.0",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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
+ },
+ "stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "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=="
+ },
+ "synckit": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
+ "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
+ "dev": true,
+ "requires": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+ },
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true
+ },
+ "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
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "tsconfck": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz",
+ "integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "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,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ }
+ }
+ },
+ "tslib": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
+ },
+ "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,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "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
+ },
+ "typed-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ }
+ },
+ "typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ }
+ },
+ "typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dev": true,
+ "requires": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ }
+ },
+ "typed-array-length": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ }
+ },
+ "typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "dev": true
+ },
+ "typescript-eslint": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.2.0.tgz",
+ "integrity": "sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/eslint-plugin": "8.2.0",
+ "@typescript-eslint/parser": "8.2.0",
+ "@typescript-eslint/utils": "8.2.0"
+ }
+ },
+ "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,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
+ "undici": {
+ "version": "6.19.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz",
+ "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A=="
+ },
+ "undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "update-browserslist-db": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "requires": {}
+ },
+ "vite": {
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz",
+ "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.21.3",
+ "fsevents": "~2.3.3",
+ "postcss": "^8.4.41",
+ "rollup": "^4.20.0"
+ },
+ "dependencies": {
+ "fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "vite-tsconfig-paths": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.0.1.tgz",
+ "integrity": "sha512-yqwv+LstU7NwPeNqajZzLEBVpUFU6Dugtb2P84FXuvaoYA+/70l9MHE+GYfYAycVyPSDYZ7mjOFuYBRqlEpTig==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^3.0.3"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "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=="
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "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,
+ "requires": {
+ "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"
+ }
+ },
+ "which-builtin-type": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz",
+ "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==",
+ "dev": true,
+ "requires": {
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "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.2",
+ "which-typed-array": "^1.1.15"
+ }
+ },
+ "which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "requires": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ }
+ },
+ "which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dev": true,
+ "requires": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "requires": {
+ "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"
+ },
+ "dependencies": {
+ "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=="
+ },
+ "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=="
+ },
+ "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==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ }
+ }
+ },
+ "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=="
+ },
+ "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
+ },
+ "zustand": {
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz",
+ "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==",
+ "requires": {
+ "use-sync-external-store": "1.2.2"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index caed79e..03ae11d 100644
--- a/package.json
+++ b/package.json
@@ -7,23 +7,59 @@
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "prepare": "husky install",
+ "test": "echo \"Error: no test specified\" && exit 0"
},
"dependencies": {
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@headlessui/react": "^2.1.3",
+ "@tanstack/react-query": "^5.53.2",
+ "axios": "^1.7.7",
+ "firebase": "^10.13.1",
+ "lucide-react": "^0.435.0",
"react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "react-dom": "^18.3.1",
+ "react-intersection-observer": "^9.13.0",
+ "react-router-dom": "^6.26.1",
+ "zustand": "^4.5.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
+ "@playwright/test": "^1.46.1",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
- "eslint": "^9.9.0",
- "eslint-plugin-react-hooks": "^5.1.0-rc.0",
+ "eslint": "^8.57.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-import-order": "^2.1.4",
+ "eslint-plugin-jsx-a11y": "^6.9.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-react": "^7.35.0",
+ "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.9.0",
+ "husky": "^8.0.0",
+ "lint-staged": "^15.2.9",
+ "prettier": "^3.3.3",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.1",
- "vite": "^5.4.1"
+ "vite": "^5.4.1",
+ "vite-tsconfig-paths": "^5.0.1"
+ },
+ "lint-staged": {
+ "*.{ts,tsx,js,jsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ],
+ "*.json": [
+ "prettier --write"
+ ],
+ "*.md": [
+ "prettier --write"
+ ]
}
}
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 0000000..8167cc7
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,15 @@
+import { defineConfig, devices } from '@playwright/test';
+
+export default defineConfig({
+ testDir: './src/tests',
+ timeout: 30000,
+ expect: {
+ timeout: 5000,
+ },
+ fullyParallel: true,
+ reporter: 'list',
+ use: {
+ ...devices['Desktop Chrome'],
+ baseURL: 'http://localhost:5173',
+ },
+});
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..935261d
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/vite.svg b/public/vite.svg
deleted file mode 100644
index e7b8dfb..0000000
--- a/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index b9d355d..0000000
--- a/src/App.css
+++ /dev/null
@@ -1,42 +0,0 @@
-#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
-}
-
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
-}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
-}
-
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
-
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
-}
-
-.card {
- padding: 2em;
-}
-
-.read-the-docs {
- color: #888;
-}
diff --git a/src/App.tsx b/src/App.tsx
index afe48ac..01d95f5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,35 +1,15 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
-import './App.css'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { RouterProvider } from 'react-router-dom';
+import { router } from '@/router/router';
+import GlobalStyles from '@/styles/GlobalStyles';
-function App() {
- const [count, setCount] = useState(0)
+const queryClient = new QueryClient();
- return (
- <>
-
- Vite + React
-
-
-
- Edit src/App.tsx and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
-}
+export const App = () => (
+
+
+
+
+);
-export default App
+export default App;
diff --git a/src/api/chennelInfo.ts b/src/api/chennelInfo.ts
new file mode 100644
index 0000000..bfb4c0f
--- /dev/null
+++ b/src/api/chennelInfo.ts
@@ -0,0 +1,65 @@
+import { useState, useEffect, useCallback } from 'react';
+import { doc, onSnapshot } from 'firebase/firestore';
+import { useParams } from 'react-router-dom';
+import { db } from '@/firebase/firbaseConfig';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+interface ChennelDataProps {
+ channelFollower: string[];
+ channelFollowing: string[];
+ channelName: string;
+ profileImg: string;
+ isMyChannel: boolean;
+}
+
+export const useChennelData = (channelUserId: string | undefined) => {
+ const [chennelData, setChennelData] = useState(null);
+ const { userId: loggedInUserId } = useAuthStore();
+ const { userId: urlUserId } = useParams<{ userId: string }>();
+
+ const fetchChennelData = useCallback(() => {
+ if (!channelUserId) {
+ setChennelData(null);
+ return () => {};
+ }
+
+ const userDocRef = doc(db, 'users', channelUserId);
+
+ const unsubscribe = onSnapshot(
+ userDocRef,
+ (docSnapshot) => {
+ if (docSnapshot.exists()) {
+ const data = docSnapshot.data();
+ const selectedData: ChennelDataProps = {
+ channelFollower: data.channelFollower || [],
+ channelFollowing: data.channelFollowing || [],
+ channelName: data.channelName || '',
+ profileImg: data.profileImg || '',
+ isMyChannel: loggedInUserId === urlUserId,
+ };
+ setChennelData(selectedData);
+ } else {
+ setChennelData(null);
+ }
+ },
+ (error) => {
+ console.error(
+ '์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค:',
+ error
+ );
+ setChennelData(null);
+ }
+ );
+
+ return unsubscribe;
+ }, [channelUserId, loggedInUserId, urlUserId]);
+
+ useEffect(() => {
+ const unsubscribe = fetchChennelData();
+ return () => {
+ if (unsubscribe) unsubscribe();
+ };
+ }, [fetchChennelData]);
+
+ return { chennelData, refetchChennelData: fetchChennelData };
+};
diff --git a/src/api/comment.ts b/src/api/comment.ts
new file mode 100644
index 0000000..a27b8a0
--- /dev/null
+++ b/src/api/comment.ts
@@ -0,0 +1,106 @@
+import {
+ addDoc,
+ collection,
+ deleteDoc,
+ doc,
+ getDoc,
+ getDocs,
+ orderBy,
+ query,
+ updateDoc,
+ where,
+} from 'firebase/firestore';
+import { COLLECTION, db } from '@/firebase/firbaseConfig';
+import { ApiResponse, UserProps } from '@/types/api';
+import { CommentProps } from '@/types/playlistType';
+
+export interface CommentWithProfileApiProps extends CommentProps {
+ docId: string;
+ imgUrl: string;
+ userName: string;
+}
+
+export const getPlaylistComment = async (
+ playlistId: number
+): Promise> => {
+ try {
+ const result = await getDocs(
+ query(
+ collection(db, COLLECTION.comments),
+ where('playlistId', '==', playlistId),
+ orderBy('regDate', 'desc')
+ )
+ );
+
+ const commentList: CommentWithProfileApiProps[] = result.docs.map(
+ (doc) => ({ ...doc.data(), docId: doc.id }) as CommentWithProfileApiProps
+ );
+
+ await Promise.all(
+ commentList.map(async (comment, index) => {
+ const docRef = doc(db, COLLECTION.users, `${comment.userId}`);
+ const docSnap = await getDoc(docRef);
+ if (!!!docSnap.exists()) {
+ return { status: 'fail' };
+ }
+
+ const data = docSnap.data() as UserProps;
+
+ commentList[index] = {
+ ...comment,
+ imgUrl: data.profileImg,
+ userName: data.channelName,
+ };
+ })
+ );
+
+ return { status: 'success', result: commentList };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+export const addComment = async (
+ commentData: CommentProps
+): Promise> => {
+ try {
+ const col = collection(db, COLLECTION.comments);
+ await addDoc(col, commentData);
+ return { status: 'success' };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+export const modifyComment = async (
+ commentData: CommentProps
+): Promise> => {
+ try {
+ const docRef = doc(db, COLLECTION.comments, `${commentData.docId}`);
+ const docSnap = await getDoc(docRef);
+
+ if (!!!docSnap.exists()) return { status: 'fail' };
+
+ await updateDoc(docRef, { ...commentData } as Omit);
+
+ return { status: 'success' };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+export const removeComment = async (
+ commentId: string
+): Promise> => {
+ try {
+ const docRef = doc(db, COLLECTION.comments, `${commentId}`);
+ await deleteDoc(docRef);
+ return { status: 'success', result: true };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
diff --git a/src/api/followingList.ts b/src/api/followingList.ts
new file mode 100644
index 0000000..9209ec9
--- /dev/null
+++ b/src/api/followingList.ts
@@ -0,0 +1,24 @@
+// src/api/followingList.ts
+import { doc, getDoc, updateDoc } from 'firebase/firestore';
+import { db } from '@/firebase/firbaseConfig';
+
+const updateUserFollowingList = async (userId: string, uid: string) => {
+ const userDocRef = doc(db, 'users', userId);
+ const userDoc = await getDoc(userDocRef);
+
+ if (userDoc.exists()) {
+ const { channelFollowing } = userDoc.data() as {
+ channelFollowing: string[];
+ };
+
+ const updatedChannelFollowing = channelFollowing.filter(
+ (followingUid: string) => followingUid !== uid
+ );
+
+ await updateDoc(userDocRef, {
+ channelFollowing: updatedChannelFollowing,
+ });
+ }
+};
+
+export default updateUserFollowingList;
diff --git a/src/api/followingPlaylists.ts b/src/api/followingPlaylists.ts
new file mode 100644
index 0000000..98b32a1
--- /dev/null
+++ b/src/api/followingPlaylists.ts
@@ -0,0 +1,92 @@
+import {
+ collection,
+ query,
+ where,
+ DocumentData,
+ getDocs,
+ Query,
+ limit,
+ orderBy,
+ doc,
+ getDoc,
+ startAfter,
+} from 'firebase/firestore';
+import { getCommentCount } from '@/api/myplaylists';
+import { db } from '@/firebase/firbaseConfig';
+import { PlayListDataProps } from '@/types/playlistType';
+
+const fetchFollowingPlaylists = async (
+ userId: string,
+ loginUserId: string,
+ pageSize: number,
+ pageParam: number
+): Promise<{
+ playlistsData: PlayListDataProps[];
+ nextCursor: number | null;
+}> => {
+ let q: Query;
+ const playlistCollection = collection(db, 'playlist');
+
+ if (userId === loginUserId) {
+ const userDocRef = doc(db, 'users', userId);
+ const userDocSnap = await getDoc(userDocRef);
+
+ if (userDocSnap.exists()) {
+ const userData = userDocSnap.data() as DocumentData;
+ const { channelFollowing } = userData;
+
+ if (channelFollowing && channelFollowing.length > 0) {
+ q = query(
+ playlistCollection,
+ where('userId', 'in', channelFollowing),
+ where('isPublic', '==', true),
+ orderBy('regDate', 'desc'),
+ limit(pageSize)
+ );
+ } else {
+ return { playlistsData: [], nextCursor: null };
+ }
+ } else {
+ return { playlistsData: [], nextCursor: null };
+ }
+ } else {
+ q = query(
+ playlistCollection,
+ where('userId', '==', userId),
+ where('isPublic', '==', true),
+ orderBy('regDate', 'desc'),
+ limit(pageSize)
+ );
+ }
+
+ if (pageParam > 0) {
+ const previousQuery = query(q, limit(pageParam * pageSize));
+ const previousSnapshot = await getDocs(previousQuery);
+ const lastDoc = previousSnapshot.docs[previousSnapshot.docs.length - 1];
+
+ if (lastDoc) {
+ q = query(q, startAfter(lastDoc));
+ }
+ }
+
+ const fetchPlaylists = async (q: Query) => {
+ const querySnapshot = await getDocs(q);
+ return await Promise.all(
+ querySnapshot.docs.map(async (doc) => ({
+ playlistId: doc.id,
+ commentCount: await getCommentCount(doc.id),
+ ...(doc.data() as Omit<
+ PlayListDataProps,
+ 'playlistId' | 'commentCount'
+ >),
+ }))
+ );
+ };
+
+ const allPlaylists = await fetchPlaylists(q);
+ const nextCursor = allPlaylists.length < pageSize ? null : pageParam + 1;
+
+ return { playlistsData: allPlaylists, nextCursor };
+};
+
+export default fetchFollowingPlaylists;
diff --git a/src/api/homePlaylists.ts b/src/api/homePlaylists.ts
new file mode 100644
index 0000000..caff182
--- /dev/null
+++ b/src/api/homePlaylists.ts
@@ -0,0 +1,87 @@
+import {
+ collection,
+ getDocs,
+ orderBy,
+ query,
+ where,
+ startAfter,
+ limit,
+ Query,
+ DocumentData,
+} from 'firebase/firestore';
+import { getCommentCount } from '@/api/myplaylists';
+import { db } from '@/firebase/firbaseConfig';
+import { PlayListDataProps } from '@/types/playlistType';
+
+export const fetchHomePlaylists = async (
+ pageParam: number,
+ sortOption: string,
+ pageSize: number
+): Promise<{
+ playlistsData: PlayListDataProps[];
+ nextCursor: number | null;
+}> => {
+ const playlistCollection = collection(db, 'playlist');
+ const isCommentSort = sortOption === '๋๊ธ์';
+ const orderByField = sortOption === '์ข์์์' ? 'likes' : 'regDate';
+
+ const fetchPlaylists = async (q: Query) => {
+ const querySnapshot = await getDocs(q);
+
+ return await Promise.all(
+ querySnapshot.docs.map(async (doc) => ({
+ playlistId: doc.id,
+ commentCount: await getCommentCount(doc.id),
+ ...(doc.data() as Omit<
+ PlayListDataProps,
+ 'playlistId' | 'commentCount'
+ >),
+ }))
+ );
+ };
+
+ if (isCommentSort) {
+ const q = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ orderBy('regDate', 'desc')
+ );
+
+ const allPlaylists = await fetchPlaylists(q);
+ allPlaylists.sort((a, b) => (b.commentCount ?? 0) - (a.commentCount ?? 0));
+
+ const startIndex = pageParam * pageSize;
+ const paginatedData = allPlaylists.slice(startIndex, startIndex + pageSize);
+ const nextCursor =
+ startIndex + pageSize < allPlaylists.length ? pageParam + 1 : null;
+
+ return { playlistsData: paginatedData, nextCursor };
+ } else {
+ let q = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ orderBy(orderByField, 'desc'),
+ limit(pageSize)
+ );
+
+ if (pageParam > 0) {
+ const previousQuery = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ orderBy(orderByField, 'desc'),
+ limit(pageParam * pageSize)
+ );
+ const previousSnapshot = await getDocs(previousQuery);
+ const lastDoc = previousSnapshot.docs[previousSnapshot.docs.length - 1];
+
+ if (lastDoc) {
+ q = query(q, startAfter(lastDoc));
+ }
+ }
+
+ const allPlaylists = await fetchPlaylists(q);
+ const nextCursor = allPlaylists.length < pageSize ? null : pageParam + 1;
+
+ return { playlistsData: allPlaylists, nextCursor };
+ }
+};
diff --git a/src/api/myplaylists.ts b/src/api/myplaylists.ts
new file mode 100644
index 0000000..282fce5
--- /dev/null
+++ b/src/api/myplaylists.ts
@@ -0,0 +1,196 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+import {
+ collection,
+ getDocs,
+ query,
+ limit,
+ startAfter,
+ QueryDocumentSnapshot,
+ DocumentData,
+ orderBy,
+ where,
+ getDoc,
+ doc,
+} from 'firebase/firestore';
+import { useParams } from 'react-router-dom';
+import { db } from '@/firebase/firbaseConfig';
+import { PlayListDataProps } from '@/types/playlistType';
+
+type PlaylistPageType = 'user' | 'liked' | 'saved';
+
+interface PlaylistsResultProps {
+ playlist: PlayListDataProps[];
+ nextCursor: QueryDocumentSnapshot | null;
+}
+
+const playlistKeys = {
+ all: ['playlist'] as const,
+ userList: (userId: string) => [...playlistKeys.all, 'user', userId] as const,
+ userLikedList: (userId: string) =>
+ [...playlistKeys.all, 'liked', userId] as const,
+ userSavedList: (userId: string) =>
+ [...playlistKeys.all, 'saved', userId] as const,
+};
+
+const getOwnerChannelName = async (userId: string): Promise => {
+ try {
+ const userDocRef = doc(db, 'users', userId);
+ const userDocSnap = await getDoc(userDocRef);
+
+ if (userDocSnap.exists()) {
+ const userData = userDocSnap.data();
+ return userData?.channelName || '์ ์ ์๋ ์ฑ๋';
+ } else {
+ console.log('userId์ ๋ํ ์ฌ์ฉ์ ๋ฌธ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค:', userId);
+ return '์ ์ ์๋ ์ฑ๋';
+ }
+ } catch (error) {
+ console.error('์ฑ๋ ์ด๋ฆ์ ๊ฐ์ ธ์ค๋ ๋์ ์ค๋ฅ ๋ฐ์:', error);
+ return '์ ์ ์๋ ์ฑ๋';
+ }
+};
+
+export const getCommentCount = async (playlistId: string): Promise => {
+ try {
+ const commentsRef = collection(db, 'comments');
+ const numericPlaylistId = parseInt(playlistId, 10);
+ if (isNaN(numericPlaylistId)) {
+ return 0;
+ }
+ const q = query(commentsRef, where('playlistId', '==', numericPlaylistId));
+ const querySnapshot = await getDocs(q);
+ const count = querySnapshot.size;
+ return count;
+ } catch (error) {
+ console.error('๋๊ธ ์ ๊ฐ์ ธ์ค๊ธฐ ์ค๋ฅ:', error);
+ return 0;
+ }
+};
+
+const fetchPlaylists = async (
+ userId: string,
+ pageParam: QueryDocumentSnapshot | null,
+ pageSize: number,
+ useDelay: boolean = false,
+ type: PlaylistPageType
+): Promise => {
+ if (useDelay) {
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ }
+
+ const userDocRef = doc(db, 'users', userId);
+ const userDocSnap = await getDoc(userDocRef);
+
+ if (!userDocSnap.exists()) {
+ throw new Error('์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.');
+ }
+
+ const userData = userDocSnap.data();
+ const playlistRef = collection(db, 'playlist');
+ let q;
+
+ switch (type) {
+ case 'user':
+ q = query(
+ playlistRef,
+ where('userId', '==', userId),
+ orderBy('regDate', 'desc'),
+ limit(pageSize)
+ );
+ break;
+ case 'liked':
+ case 'saved':
+ const playlistIdStrings =
+ (type === 'liked' ? userData.likedPlaylist : userData.savedPlaylist) ||
+ [];
+
+ if (playlistIdStrings.length === 0) {
+ return { playlist: [], nextCursor: null };
+ }
+
+ q = query(
+ playlistRef,
+ where('__name__', 'in', playlistIdStrings.map(String)),
+ orderBy('regDate', 'desc'),
+ limit(pageSize)
+ );
+ break;
+ default:
+ throw new Error(`์๋ชป๋ ์ฌ์ ๋ชฉ๋ก ์ ํ: ${type}`);
+ }
+
+ if (pageParam) {
+ q = query(q, startAfter(pageParam));
+ }
+
+ const querySnapshot = await getDocs(q);
+ const playlistPromises = querySnapshot.docs.map(async (doc) => {
+ const data = doc.data();
+ const ownerChannelName = await getOwnerChannelName(data.userId);
+ const commentCount = await getCommentCount(doc.id);
+
+ return {
+ playlistId: doc.id,
+ title: data.title,
+ isPublic: data.isPublic,
+ likes: data.likes,
+ links: data.links,
+ regDate: data.regDate,
+ tags: data.tags,
+ thumbnail: data.thumbnail,
+ userId: data.userId,
+ ownerChannelName,
+ commentCount,
+ description: data.description,
+ };
+ });
+
+ const playlist: PlayListDataProps[] = await Promise.all(playlistPromises);
+ const nextCursor = querySnapshot.docs[querySnapshot.docs.length - 1] || null;
+
+ return { playlist, nextCursor };
+};
+
+const createPlaylistQuery =
+ (type: PlaylistPageType) =>
+ (pageSize: number, useDelay: boolean = false) => {
+ const { userId } = useParams<{ userId: string }>();
+
+ let queryKeyFn;
+ switch (type) {
+ case 'user':
+ queryKeyFn = playlistKeys.userList;
+ break;
+ case 'liked':
+ queryKeyFn = playlistKeys.userLikedList;
+ break;
+ case 'saved':
+ queryKeyFn = playlistKeys.userSavedList;
+ break;
+ default:
+ throw new Error(`์๋ชป๋ ์ฌ์ ๋ชฉ๋ก ์ ํ: ${type}`);
+ }
+
+ return useInfiniteQuery({
+ queryKey: queryKeyFn(userId || ''),
+ queryFn: ({ pageParam }) => {
+ if (!userId) {
+ throw new Error('URL์ ์ฌ์ฉ์ ID๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค.');
+ }
+ return fetchPlaylists(
+ userId,
+ pageParam as QueryDocumentSnapshot | null,
+ pageSize,
+ useDelay,
+ type
+ );
+ },
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ initialPageParam: null as QueryDocumentSnapshot | null,
+ enabled: !!userId,
+ });
+ };
+
+export const useFetchUserPlaylist = createPlaylistQuery('user');
+export const useFetchLikedPlaylist = createPlaylistQuery('liked');
+export const useFetchSavedPlaylist = createPlaylistQuery('saved');
diff --git a/src/api/playlistActions.ts b/src/api/playlistActions.ts
new file mode 100644
index 0000000..6214bce
--- /dev/null
+++ b/src/api/playlistActions.ts
@@ -0,0 +1,72 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { doc, updateDoc } from 'firebase/firestore';
+import { db } from '@/firebase/firbaseConfig';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+type PlaylistType = 'liked' | 'saved';
+
+export const updateUserPlaylists = async (
+ userId: string,
+ playlistType: PlaylistType,
+ playlistData: number[]
+): Promise => {
+ const userDocRef = doc(db, 'users', userId);
+
+ const updateData =
+ playlistType === 'liked'
+ ? { likedPlaylist: playlistData }
+ : { savedPlaylist: playlistData };
+
+ await updateDoc(userDocRef, updateData);
+};
+
+export const updatePlaylistLikes = async (
+ playlistId: number,
+ newLikes: number
+) => {
+ const playlistDocRef = doc(db, 'playlist', playlistId.toString());
+
+ await updateDoc(playlistDocRef, {
+ likes: newLikes,
+ });
+};
+
+export const useUpdateUserPlaylists = () => {
+ const { user, userId, likedPlaylist, savedPlaylist } = useAuthStore();
+ const queryClient = useQueryClient();
+
+ const { mutate: updatePlaylists } = useMutation({
+ mutationFn: async ({
+ playlistType,
+ playlistId,
+ newLikes,
+ }: {
+ playlistType: PlaylistType;
+ playlistId: number;
+ newLikes?: number;
+ }) => {
+ if (!user) {
+ throw new Error('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.');
+ }
+
+ const playlistData =
+ playlistType === 'liked' ? likedPlaylist : savedPlaylist;
+
+ await updateUserPlaylists(userId, playlistType, playlistData);
+
+ if (newLikes !== undefined) {
+ await updatePlaylistLikes(playlistId, newLikes);
+ }
+ },
+ onSuccess: () => {
+ if (user) {
+ queryClient.invalidateQueries({ queryKey: ['user', userId] });
+ }
+ },
+ onError: (error: Error) => {
+ console.error(error.message);
+ },
+ });
+
+ return { updatePlaylists };
+};
diff --git a/src/api/playlistInfo.ts b/src/api/playlistInfo.ts
new file mode 100644
index 0000000..035a0d0
--- /dev/null
+++ b/src/api/playlistInfo.ts
@@ -0,0 +1,134 @@
+import {
+ collection,
+ deleteDoc,
+ doc,
+ DocumentData,
+ getDoc,
+ getDocs,
+ query,
+ QueryDocumentSnapshot,
+ setDoc,
+ updateDoc,
+} from 'firebase/firestore';
+import {
+ deleteObject,
+ getDownloadURL,
+ listAll,
+ ref,
+ uploadBytes,
+} from 'firebase/storage';
+import { COLLECTION, db, storage, STORAGE } from '@/firebase/firbaseConfig';
+import { ApiResponse } from '@/types/api';
+import { PlayListDataProps } from '@/types/playlistType';
+
+export const getPlaylistInfo = async (
+ playlistId: number
+): Promise> => {
+ try {
+ const docRef = doc(db, COLLECTION.playlist, `${playlistId}`);
+ const docSnap = await getDoc(docRef);
+ return { status: 'success', result: docSnap.data() as PlayListDataProps };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+export const addPlaylist = async (
+ data: PlayListDataProps
+): Promise> => {
+ const col = collection(db, COLLECTION.playlist);
+ const { docs } = await getDocs(query(col));
+ const nextPlaylistId = getNextPlaylistId(docs);
+
+ const addData: PlayListDataProps = { ...data };
+ delete addData.thumbnailFile;
+
+ await setDoc(doc(db, COLLECTION.playlist, `${nextPlaylistId}`), addData);
+
+ const docRef = doc(db, COLLECTION.playlist, `${nextPlaylistId}`);
+
+ if (data.thumbnailFile) {
+ const url = await uploadThumbnail(
+ data.thumbnailFile,
+ nextPlaylistId.toString()
+ );
+
+ await updateDoc(docRef, { thumbnail: url });
+ }
+
+ return { status: 'success', result: true };
+};
+
+export const updatePlaylist = async (
+ data: PlayListDataProps,
+ playlistId: number
+): Promise> => {
+ const docRef = doc(db, COLLECTION.playlist, `${playlistId}`);
+ const docSnap = await getDoc(docRef);
+
+ const addData: PlayListDataProps = { ...data };
+ delete addData.thumbnailFile;
+
+ if (!!!docSnap.exists()) return { status: 'fail' };
+ if (data.thumbnailFile) {
+ const url = await uploadThumbnail(data.thumbnailFile, `${playlistId}`);
+ addData.thumbnail = url;
+ }
+ await updateDoc(docRef, { ...addData });
+ return { status: 'success', result: true };
+};
+
+export const deletePlaylist = async (
+ playlistId: number
+): Promise> => {
+ const docRef = doc(db, COLLECTION.playlist, `${playlistId}`);
+ try {
+ await deleteDoc(docRef);
+ await removeThumbnail(`${playlistId}`);
+ return { status: 'success', result: true };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+const getNextPlaylistId = (
+ docs: QueryDocumentSnapshot[]
+): number => {
+ const sortedDocs = docs;
+ sortedDocs.sort((a, b) => Number(b.id) - Number(a.id));
+ const result = Number(sortedDocs[0].id) ?? 0;
+ return result + 1;
+};
+
+const uploadThumbnail = async (file: File, docId: string) => {
+ const path = `${STORAGE.playlist}/${docId}/thumbnail.png`;
+ const locationRef = ref(storage, path);
+
+ const result = await uploadBytes(locationRef, file);
+ const url = await getDownloadURL(result.ref);
+
+ return url;
+};
+
+const removeThumbnail = async (docId: string): Promise => {
+ const path = `${STORAGE.playlist}/${docId}`;
+ const locationRef = ref(storage, path);
+
+ try {
+ listAll(locationRef).then((dir) => {
+ dir.items.forEach(async (fileRef) => {
+ await deleteObject(fileRef);
+ });
+ dir.prefixes.forEach(async (folderRef) => {
+ await deleteObject(folderRef);
+ });
+ });
+
+ return true;
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+};
diff --git a/src/api/profileInfo.ts b/src/api/profileInfo.ts
new file mode 100644
index 0000000..2946516
--- /dev/null
+++ b/src/api/profileInfo.ts
@@ -0,0 +1,120 @@
+import {
+ collection,
+ query,
+ where,
+ getDocs,
+ doc,
+ getDoc,
+ updateDoc,
+} from 'firebase/firestore';
+
+import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
+import { db, storage } from '@/firebase/firbaseConfig';
+import { ApiResponse, UserProps } from '@/types/api';
+
+interface ProfileUpdateData {
+ profileImageFile?: File;
+}
+
+export const getUserInfo = async (
+ userId: string
+): Promise> => {
+ try {
+ const userDocRef = doc(db, 'users', userId);
+ const docSnap = await getDoc(userDocRef);
+
+ if (!!!docSnap) throw new Error('์ฌ์ฉ์ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ ์คํจ');
+
+ return { status: 'success', result: docSnap.data() as UserProps };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
+
+const getUserComments = async (userId: string) => {
+ try {
+ const comments = collection(db, 'comments');
+ const q = query(comments, where('userId', '==', userId));
+ const querySnapshot = await getDocs(q);
+ const userComments = querySnapshot.docs.map((doc) => ({
+ commentsId: doc.id,
+ content: doc.data().content,
+ playlistId: doc.data().playlistId,
+ }));
+ return userComments;
+ } catch (error) {
+ console.error('comments ๊ฐ์ ธ์ค๊ธฐ ์๋ฌ', error);
+ return [];
+ }
+};
+
+const getPlaylistTitle = async (playlistId: number) => {
+ try {
+ const playlistIdDoc = doc(db, 'playlist', playlistId.toString());
+ const playlistIdDocSnap = await getDoc(playlistIdDoc);
+ const playlistData = playlistIdDocSnap.data();
+ return playlistData?.title;
+ } catch (error) {
+ console.error('title ๊ฐ์ ธ์ค๊ธฐ ์๋ฌ', error);
+ }
+};
+
+const getMyPlaylistCount = async (userId: string) => {
+ try {
+ const playlists = collection(db, 'playlist');
+ const q = query(playlists, where('userId', '==', userId));
+ const querySnapshot = await getDocs(q);
+ return querySnapshot.size;
+ } catch (error) {
+ console.error('๋ด๊ฐ ๋ง๋ ํ๋ ์ด๋ฆฌ์คํธ ์ ๊ฐ์ ธ์ค๊ธฐ ์๋ฌ', error);
+ }
+};
+
+const getMyHashtag = async (userId: string) => {
+ const userDoc = doc(db, 'users', userId);
+ const userSnapshot = await getDoc(userDoc);
+ if (userSnapshot.exists()) {
+ const userData = userSnapshot.data();
+ const hashtags = userData.tags;
+ return hashtags;
+ }
+};
+
+const updateProfileTags = async (userId: string, tags: string[]) => {
+ const userDoc = doc(db, 'users', userId);
+ await updateDoc(userDoc, {
+ tags,
+ });
+};
+
+const uploadImage = async (file: File, userId: string) => {
+ const path = `profile/${userId}/profile.png`;
+ const locationRef = ref(storage, path);
+ const result = await uploadBytes(locationRef, file);
+ const url = await getDownloadURL(result.ref);
+ return url;
+};
+
+const updateProfileImage = async (userId: string, data: ProfileUpdateData) => {
+ const docRef = doc(db, 'users', userId);
+ const docSnap = await getDoc(docRef);
+ if (!docSnap.exists()) {
+ return { status: 'fail', result: false };
+ }
+ const updatedData: { profileImg?: string } = {};
+ if (data.profileImageFile) {
+ const imageUrl = await uploadImage(data.profileImageFile, userId);
+ updatedData.profileImg = imageUrl;
+ }
+ await updateDoc(docRef, updatedData);
+};
+
+export {
+ getUserComments,
+ getPlaylistTitle,
+ getMyPlaylistCount,
+ updateProfileImage,
+ getMyHashtag,
+ updateProfileTags,
+};
diff --git a/src/api/searchPlaylists.ts b/src/api/searchPlaylists.ts
new file mode 100644
index 0000000..b2d06d7
--- /dev/null
+++ b/src/api/searchPlaylists.ts
@@ -0,0 +1,93 @@
+import {
+ collection,
+ getDocs,
+ orderBy,
+ query,
+ where,
+ startAfter,
+ QueryDocumentSnapshot,
+ DocumentData,
+ limit,
+} from 'firebase/firestore';
+import { getCommentCount } from '@/api/myplaylists';
+import { db } from '@/firebase/firbaseConfig';
+import { PlayListDataProps } from '@/types/playlistType';
+
+const fetchPlaylists = async (
+ keyword: string,
+ pageParam: QueryDocumentSnapshot | null,
+ pageSize: number
+) => {
+ const playlistCollection = collection(db, 'playlist');
+ let q = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ orderBy('title'),
+ orderBy('regDate', 'desc'),
+ limit(pageSize)
+ );
+
+ if (pageParam) {
+ q = query(q, startAfter(pageParam));
+ }
+
+ const querySnapshot = await getDocs(q);
+ const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1] || null;
+
+ const allPlaylists = await Promise.all(
+ querySnapshot.docs.map(async (doc) => {
+ const commentCount = await getCommentCount(doc.id);
+
+ return {
+ playlistId: doc.id,
+ commentCount,
+ ...(doc.data() as Omit<
+ PlayListDataProps,
+ 'playlistId' | 'commentCount'
+ >),
+ };
+ })
+ );
+
+ let filteredData;
+
+ if (keyword.startsWith('#')) {
+ filteredData = allPlaylists.filter((playlist) =>
+ playlist.tags?.includes(keyword)
+ );
+ } else {
+ filteredData = allPlaylists.filter((playlist) =>
+ playlist.title.toLowerCase().includes(keyword.toLowerCase())
+ );
+ }
+
+ return { filteredData, lastVisible };
+};
+
+export const fetchSearchPlaylists = async (
+ keyword: string,
+ pageParam: QueryDocumentSnapshot | null,
+ pageSize: number = 5
+): Promise<{
+ playlistsData: PlayListDataProps[];
+ nextCursor: QueryDocumentSnapshot | null;
+}> => {
+ const allPlaylistsData: PlayListDataProps[] = [];
+ let lastVisible: QueryDocumentSnapshot | null = pageParam;
+
+ do {
+ const { filteredData, lastVisible: newLastVisible } = await fetchPlaylists(
+ keyword,
+ lastVisible,
+ pageSize
+ );
+
+ allPlaylistsData.push(...filteredData);
+ lastVisible = newLastVisible;
+ } while (allPlaylistsData.length < pageSize && lastVisible);
+
+ return {
+ playlistsData: allPlaylistsData.slice(0, pageSize),
+ nextCursor: allPlaylistsData.length < pageSize ? null : lastVisible,
+ };
+};
diff --git a/src/api/video.ts b/src/api/video.ts
new file mode 100644
index 0000000..278607f
--- /dev/null
+++ b/src/api/video.ts
@@ -0,0 +1,32 @@
+import axios from 'axios';
+import { AddedLinkProps } from '@/components/playlist/AddedVideo';
+import { ApiResponse } from '@/types/api';
+import { getVideoId } from '@/utils/videoUtils';
+
+export const getVideoInfo = async (
+ link: string
+): Promise> => {
+ try {
+ const { data } = await axios.get(`https://noembed.com/embed?url=${link}`);
+ if (!!!data) throw new Error('๋งํฌ ๋ณํ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.');
+
+ if (!!data.error) throw new Error('๋งํฌ ๋ณํ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.');
+
+ return {
+ status: 'success',
+ result: {
+ videoId: data.video_id ?? getVideoId(link),
+ onRemove: () => {},
+ imgUrl: data.thumbnail_url,
+ link,
+ title: data.title,
+ userName: data.author_name,
+ embedCode: data.html,
+ provider: data.provider_name,
+ },
+ };
+ } catch (err) {
+ console.error(err);
+ return { status: 'fail' };
+ }
+};
diff --git a/src/assets/404.png b/src/assets/404.png
new file mode 100644
index 0000000..a0ad7c4
Binary files /dev/null and b/src/assets/404.png differ
diff --git a/src/assets/defaultThumbnail.jpg b/src/assets/defaultThumbnail.jpg
new file mode 100644
index 0000000..407d6f3
Binary files /dev/null and b/src/assets/defaultThumbnail.jpg differ
diff --git a/src/assets/defaultThumbnail2.jpg b/src/assets/defaultThumbnail2.jpg
new file mode 100644
index 0000000..fd72d68
Binary files /dev/null and b/src/assets/defaultThumbnail2.jpg differ
diff --git a/src/assets/folderIcon.png b/src/assets/folderIcon.png
new file mode 100644
index 0000000..469d4b6
Binary files /dev/null and b/src/assets/folderIcon.png differ
diff --git a/src/assets/google_icon.svg b/src/assets/google_icon.svg
new file mode 100644
index 0000000..e272220
--- /dev/null
+++ b/src/assets/google_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/logo.svg b/src/assets/logo.svg
new file mode 100644
index 0000000..c859b30
--- /dev/null
+++ b/src/assets/logo.svg
@@ -0,0 +1,21 @@
+
diff --git a/src/assets/logoIcon.png b/src/assets/logoIcon.png
new file mode 100644
index 0000000..7ba850c
Binary files /dev/null and b/src/assets/logoIcon.png differ
diff --git a/src/assets/logoTitle.png b/src/assets/logoTitle.png
new file mode 100644
index 0000000..d1d5826
Binary files /dev/null and b/src/assets/logoTitle.png differ
diff --git a/src/assets/profile_default.png b/src/assets/profile_default.png
new file mode 100644
index 0000000..e4a9982
Binary files /dev/null and b/src/assets/profile_default.png differ
diff --git a/src/assets/react.svg b/src/assets/react.svg
deleted file mode 100644
index 6c87de9..0000000
--- a/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/assets/soundcloud.png b/src/assets/soundcloud.png
new file mode 100644
index 0000000..5cb7e31
Binary files /dev/null and b/src/assets/soundcloud.png differ
diff --git a/src/assets/vimeo.png b/src/assets/vimeo.png
new file mode 100644
index 0000000..a439c3d
Binary files /dev/null and b/src/assets/vimeo.png differ
diff --git a/src/assets/youtube.png b/src/assets/youtube.png
new file mode 100644
index 0000000..5abdb7b
Binary files /dev/null and b/src/assets/youtube.png differ
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
new file mode 100644
index 0000000..0e6af16
--- /dev/null
+++ b/src/components/Button.tsx
@@ -0,0 +1,149 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+
+type ButtonShapeType = 'block' | 'line' | 'round' | 'text';
+type ButtonSizeType = 'sm' | 'md' | 'lg';
+type ButtonColorType = 'primary' | 'lightGray' | 'gray' | 'red' | 'black';
+type ButtonIconType = React.ComponentType | string | undefined;
+type ButtonActionType = 'submit' | 'button' | 'reset';
+
+interface IconProps {
+ size?: string | number;
+}
+
+interface ButtonProps {
+ label: string;
+ onClick: React.MouseEventHandler;
+ shape?: ButtonShapeType;
+ size?: ButtonSizeType;
+ color?: ButtonColorType;
+ IconComponent?: ButtonIconType;
+ fullWidth?: boolean;
+ type?: ButtonActionType;
+ disabled?: boolean;
+}
+
+type ButtonColors = {
+ color: string;
+ hoverColor: string;
+};
+
+const buttonColors: Record = {
+ primary: { color: colors.primaryNormal, hoverColor: colors.primaryDark },
+ lightGray: { color: colors.gray02, hoverColor: colors.primaryNormal },
+ gray: { color: colors.gray05, hoverColor: colors.gray06 },
+ red: { color: colors.redNormal, hoverColor: colors.redDark },
+ black: { color: colors.black, hoverColor: colors.gray04 },
+};
+
+const buttonSizes: Record> = {
+ sm: css`
+ padding: 4px 12px;
+ `,
+ md: css`
+ padding: 8px 12px;
+ `,
+ lg: css`
+ padding: 14px 12px;
+ `,
+};
+
+const Button: React.FC = ({
+ label,
+ onClick,
+ shape = 'block',
+ size = 'md',
+ color = 'primary',
+ IconComponent = undefined,
+ type = 'button',
+ fullWidth = false,
+ disabled = false,
+}) => {
+ const selectColors = buttonColors[color];
+ const selectSizes = buttonSizes[size];
+
+ return (
+
+ );
+};
+
+const buttonStyle = (
+ shape: ButtonShapeType,
+ selectSizes: ReturnType,
+ selectColors: ButtonColors,
+ fullWidth: boolean,
+ disabled: boolean
+) => {
+ const backgroundColor = disabled
+ ? colors.gray03
+ : shape === 'text' || shape === 'line'
+ ? 'transparent'
+ : selectColors.color;
+
+ const textColor =
+ disabled || selectColors.color === colors.gray02
+ ? colors.black
+ : shape === 'text' || shape === 'line'
+ ? selectColors.color
+ : colors.white;
+
+ const borderColor = shape === 'line' ? selectColors.color : 'none';
+
+ const hoverStyles =
+ !disabled &&
+ css`
+ background-color: ${shape === 'text'
+ ? 'transparent'
+ : selectColors.hoverColor};
+ color: ${shape === 'text' ? selectColors.hoverColor : colors.white};
+ ${shape === 'line' && `border-color: ${selectColors.hoverColor}`};
+ `;
+
+ return css`
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ cursor: ${disabled ? 'not-allowed' : 'pointer'};
+ border: ${borderColor};
+ outline: none;
+ width: ${fullWidth ? '100%' : 'auto'};
+ border-radius: ${shape === 'round' ? '50px' : '6px'};
+
+ ${selectSizes}
+
+ background-color: ${backgroundColor};
+ color: ${textColor};
+ opacity: ${disabled ? 0.7 : 1};
+
+ &:hover {
+ ${hoverStyles}
+ }
+
+ span {
+ display: inline-flex;
+ }
+ `;
+};
+
+export default Button;
diff --git a/src/components/CheckBox.tsx b/src/components/CheckBox.tsx
new file mode 100644
index 0000000..160dedf
--- /dev/null
+++ b/src/components/CheckBox.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import { Check, Square } from 'lucide-react';
+import colors from '@/constants/colors';
+
+interface CheckboxProps {
+ checked: boolean;
+ onChange: (checked: boolean) => void;
+}
+
+const CheckBox: React.FC = ({ checked = false, onChange }) => {
+ const onCheckBoxChange = () => {
+ onChange(!checked);
+ };
+
+ return (
+
+
+ {checked && }
+
+ );
+};
+
+const checkBoxContainer = css`
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 24px;
+ height: 24px;
+`;
+
+const checkBoxStyle = css`
+ position: absolute;
+ top: 0;
+ left: 0;
+`;
+
+const checkStyle = css`
+ position: absolute;
+ background-color: ${colors.primaryLight};
+ border-radius: 2px;
+ top: 2px;
+ left: 2px;
+`;
+
+export default CheckBox;
diff --git a/src/components/Comment.tsx b/src/components/Comment.tsx
new file mode 100644
index 0000000..caef719
--- /dev/null
+++ b/src/components/Comment.tsx
@@ -0,0 +1,108 @@
+import { css } from '@emotion/react';
+import KebabButton from '@/components/KebabButton';
+import { fontSize, fontWeight } from '@/constants/font';
+import useModalStore from '@/stores/useModalStore';
+
+export interface CommentWithProfileProps {
+ imgUrl: string;
+ userName: string;
+ content: string;
+ showKebabMenu?: boolean;
+ isEdited?: boolean;
+ docId?: string;
+ onDelete?: (commentId: string) => void;
+ onClick?: () => void;
+}
+
+const Comment: React.FC = ({
+ imgUrl,
+ userName,
+ content,
+ showKebabMenu = false,
+ isEdited = false,
+ docId,
+ onDelete = () => {},
+ onClick = () => {},
+}) => {
+ const { openModal } = useModalStore();
+
+ const onDeleteBtnClick = () => {
+ openModal({
+ type: 'delete',
+ title: '๋๊ธ ์ญ์ ',
+ content: `๋๊ธ์ ์ญ์ ํ์๊ฒ ์ต๋๊น?`,
+ onAction: () => {
+ if (docId) onDelete(docId);
+ },
+ });
+ };
+
+ const menuItems = [
+ {
+ label: '์ญ์ ',
+ onClick: onDeleteBtnClick,
+ },
+ ];
+
+ return (
+
+

{
+ currentTarget.onerror = null; // prevents looping
+ currentTarget.src = '/src/assets/logoIcon.png';
+ }}
+ />
+
+
+ {userName}
+
+ {content}
+
+
+ {showKebabMenu &&
}
+
+ );
+};
+
+const commentStyles = css`
+ display: flex;
+ gap: 10px;
+ align-items: flex-start;
+
+ img {
+ min-width: 36px;
+ max-width: 36px;
+ min-height: 36px;
+ max-height: 36px;
+ border-radius: 50%;
+ object-fit: cover;
+ :hover {
+ cursor: pointer;
+ }
+ }
+`;
+
+const contentStyles = css`
+ gap: 6px;
+ display: flex;
+ flex-direction: column;
+ font-size: ${fontSize.sm};
+
+ .userName {
+ cursor: pointer;
+ font-weight: ${fontWeight.semiBold};
+ }
+
+ .content {
+ line-height: 19px;
+ }
+`;
+
+const emptyBoxStyle = css`
+ flex-grow: 1;
+`;
+
+export default Comment;
diff --git a/src/components/HashTag.tsx b/src/components/HashTag.tsx
new file mode 100644
index 0000000..314b273
--- /dev/null
+++ b/src/components/HashTag.tsx
@@ -0,0 +1,96 @@
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import useTagSelection from '@/hooks/useTagSelection';
+
+export interface Tag {
+ id: number;
+ label: string;
+ removable: boolean;
+}
+
+interface TagProps {
+ tags: Tag[];
+ onRemove: (label: string) => void;
+ margin?: string;
+ onTagClick?: (label: string, removable: boolean) => void;
+}
+
+const HashTag: React.FC = ({
+ tags,
+ onRemove,
+ margin = '3px',
+ onTagClick,
+}) => {
+ const { selectedTags, onTagSelection } = useTagSelection();
+
+ const onSelectedTagClick = (label: string, removable: boolean) => {
+ onTagSelection(label, removable);
+ if (onTagClick) {
+ onTagClick(label, removable);
+ }
+ };
+
+ return (
+
+ {tags.map((tag) => (
+
onSelectedTagClick(tag.label, tag.removable)}
+ >
+ {tag.label}
+ {tag.removable && (
+
+ )}
+
+ ))}
+
+ );
+};
+
+const tagStyle = (
+ isSelected: boolean,
+ removable: boolean,
+ margin: string
+) => css`
+ display: inline-flex;
+ align-items: center;
+ margin: ${margin};
+ border: 1px solid ${colors.gray02};
+ font-size: ${fontSize.sm};
+ font-weight: ${fontWeight.medium};
+ background-color: ${isSelected ? colors.primaryNormal : `none`};
+ color: ${isSelected ? 'white' : 'black'};
+ border-radius: 50px;
+ padding: 8px 12px;
+ cursor: ${removable ? 'default' : 'pointer'};
+ transition: background-color 0.5s;
+
+ &:hover {
+ background-color: ${isSelected ? colors.primaryNormal : `none`};
+ }
+`;
+
+const removeButtonStyle = css`
+ font-size: ${fontSize.xs};
+ margin-left: 10px;
+ background: none;
+ border: none;
+ cursor: pointer;
+`;
+
+export default HashTag;
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
new file mode 100644
index 0000000..784447d
--- /dev/null
+++ b/src/components/Header.tsx
@@ -0,0 +1,154 @@
+import React, { useEffect, useState } from 'react';
+import { css } from '@emotion/react';
+import { ChevronLeft, SearchIcon } from 'lucide-react';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
+import defaultProfile from '@/assets/profile_default.png';
+import IconButton from '@/components/IconButton';
+import Logo from '@/components/Logo';
+import Profile from '@/components/Profile';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import { useAuthStore } from '@/stores/useAuthStore';
+import useModalStore from '@/stores/useModalStore';
+import { HeaderProps } from '@/types/header';
+
+const Header: React.FC = ({ type, headerTitle }) => {
+ const [searchText, setSearchText] = useState('');
+ const { userId, user, profileImage } = useAuthStore();
+ const { keyword } = useParams<{ keyword?: string }>();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { openModal, closeModal } = useModalStore();
+
+ useEffect(() => {
+ if (location.pathname.startsWith('/search/') && keyword) {
+ setSearchText(decodeURIComponent(keyword));
+ }
+ }, [location.pathname, keyword]);
+
+ const onProfileClick = () => {
+ if (user) {
+ navigate(ROUTES.PROFILE(userId));
+ } else {
+ navigate(ROUTES.SIGN_IN);
+ }
+ };
+
+ const onSearchSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (searchText.trim()) {
+ const encodedKeyword = encodeURIComponent(searchText.trim());
+ navigate(ROUTES.SEARCH(encodedKeyword));
+ }
+ };
+
+ const onModifyBackButton = () => {
+ if (headerTitle === 'ํ๋กํ ์์ ') {
+ openModal({
+ type: 'confirm',
+ title: '์๋ฆผ',
+ content: 'ํ๋กํ ๋ณ๊ฒฝ์ฌํญ์ ์ ์ฉํ์ง ์์ผ์๊ฒ ์ต๋๊น?',
+ onAction: () => {
+ closeModal();
+ navigate(-1);
+ },
+ });
+ } else {
+ navigate(-1);
+ }
+ };
+
+ return (
+
+ {type === 'main' ? (
+
+ ) : (
+
+ )}
+ {type !== 'detail' ? (
+ <>
+
+
+ >
+ ) : (
+ {headerTitle}
+ )}
+
+ );
+};
+
+const headerStyle = css`
+ width: 430px;
+ height: 60px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 20px;
+ background-color: ${colors.white};
+ gap: 10px;
+ position: fixed;
+ z-index: 10;
+`;
+
+const searchBarStyle = css`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ background-color: ${colors.gray01};
+ border-radius: 6px;
+ padding: 2px 10px;
+
+ button {
+ padding: 0px;
+ }
+`;
+
+const searchInputStyle = css`
+ flex: 1;
+ border: none;
+ background: none;
+ font-size: ${fontSize.sm};
+ padding: 5px;
+ outline: none;
+
+ &::placeholder {
+ color: ${colors.gray03};
+ }
+`;
+
+const profileStyle = css`
+ cursor: pointer;
+`;
+
+const headerTitleStyle = css`
+ position: absolute;
+ font-size: ${fontSize.lg};
+ font-weight: ${fontWeight.semiBold};
+ left: 50%;
+ transform: translateX(-50%);
+`;
+
+export default Header;
diff --git a/src/components/IconButton.tsx b/src/components/IconButton.tsx
new file mode 100644
index 0000000..b35c139
--- /dev/null
+++ b/src/components/IconButton.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+
+type IconButtonSizeType = 'sm' | 'md';
+type IconButtonColorType = 'primary' | 'gray' | 'red' | 'black';
+
+interface IconProps {
+ size?: string | number;
+ fill?: string;
+}
+
+interface IconButtonProps {
+ IconComponent: React.ComponentType;
+ onClick: React.MouseEventHandler;
+ label?: string;
+ size?: IconButtonSizeType;
+ color?: IconButtonColorType;
+ labelColor?: IconButtonColorType;
+ fillColor?: IconButtonColorType;
+}
+
+type IconButtonColors = {
+ color: string;
+};
+
+const iconButtonColors: Record = {
+ primary: { color: colors.primaryNormal },
+ gray: { color: colors.gray05 },
+ red: { color: colors.redNormal },
+ black: { color: colors.black },
+};
+
+const IconButton: React.FC = ({
+ IconComponent,
+ onClick,
+ label = '',
+ size = 'md',
+ color = 'gray',
+ labelColor,
+ fillColor,
+}) => {
+ const selectColors = iconButtonColors[color].color;
+ const selectLabelColor = labelColor
+ ? iconButtonColors[labelColor].color
+ : selectColors;
+ const selectFillColor = fillColor
+ ? iconButtonColors[fillColor].color
+ : 'none';
+
+ return (
+
+ );
+};
+
+const iconButtonStyle = (size: IconButtonSizeType, selectColors: string) => css`
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ border: none;
+ background-color: transparent;
+ gap: 2px;
+ outline: none;
+ font-size: ${size === 'md' ? fontSize.xs : fontSize.xxs};
+ color: ${selectColors};
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
+
+const labelStyle = (selectLabelColor: string) => css`
+ color: ${selectLabelColor};
+`;
+
+export default IconButton;
diff --git a/src/components/InputBox.tsx b/src/components/InputBox.tsx
new file mode 100644
index 0000000..f4ef4fa
--- /dev/null
+++ b/src/components/InputBox.tsx
@@ -0,0 +1,176 @@
+import React, { useState, useEffect } from 'react';
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+
+interface InputBoxProps {
+ label: string;
+ description?: string;
+ placeholder: string;
+ validate?: (value: string) => string;
+ isPassword?: boolean;
+ isTextarea?: boolean;
+ labelStyle?: React.CSSProperties;
+ width?: string;
+ height?: string;
+ value?: string;
+ onChange?: (
+ e: React.ChangeEvent
+ ) => void;
+ externalErrorMessage?: string;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
+}
+
+const InputBox: React.FC = ({
+ label,
+ description,
+ placeholder,
+ validate,
+ isPassword = false,
+ isTextarea = false,
+ labelStyle,
+ width = '390px',
+ height = '36px',
+ value: propValue,
+ onChange: propOnChange,
+ externalErrorMessage,
+ onKeyDown,
+}) => {
+ const [value, setValue] = useState(propValue || '');
+ const [isTouched, setIsTouched] = useState(false);
+ const [errorMessage, setErrorMessage] = useState('');
+
+ useEffect(() => {
+ setValue(propValue || '');
+ }, [propValue]);
+
+ const onInputChange = (
+ e: React.ChangeEvent
+ ) => {
+ const inputValue = e.target.value;
+ setValue(inputValue);
+ if (propOnChange) propOnChange(e);
+
+ if (validate) {
+ const validationMessage = validate(inputValue);
+ setErrorMessage(validationMessage);
+ }
+ };
+
+ const onInputBlur = () => {
+ setIsTouched(true);
+ if (validate) {
+ const validationMessage = validate(value);
+ setErrorMessage(validationMessage);
+ }
+ };
+
+ const errorMessageToShow = externalErrorMessage || errorMessage;
+ const isValid = validate
+ ? errorMessageToShow === '' ||
+ errorMessageToShow.includes('๊ฐ๋ฅํ') ||
+ errorMessageToShow.includes('์ผ์น')
+ : null;
+
+ return (
+ <>
+
+
+ {description && }
+ {isTextarea ? (
+
+ ) : (
+
+ )}
+ {isTouched && errorMessageToShow && (
+ {errorMessageToShow}
+ )}
+
+ >
+ );
+};
+
+const inputContainerStyle = css`
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 20px;
+`;
+
+const labelStyleBase = css`
+ font-size: ${fontSize.sm};
+ font-weight: ${fontWeight.regular};
+ margin-bottom: 4px;
+`;
+
+const labelStyleDesc = css`
+ font-size: ${fontSize.sm};
+ color: ${colors.gray04};
+ margin-bottom: 4px;
+`;
+
+const inputStyle = (
+ isValid: boolean | null,
+ isTouched: boolean,
+ width: string,
+ height: string,
+ isTextarea: boolean
+) => css`
+ width: ${width};
+ height: ${height};
+ padding: 8px 12px;
+ border: 1px solid
+ ${!isTouched
+ ? colors.gray02
+ : isValid === null
+ ? colors.gray02
+ : isValid
+ ? colors.primaryNormal
+ : colors.redNormal};
+ border-radius: 6px;
+ font-size: ${fontSize.sm};
+ resize: ${isTextarea ? 'vertical' : 'none'};
+
+ &::placeholder {
+ color: ${colors.black};
+ font-size: ${fontSize.sm};
+ opacity: 0.5;
+ }
+
+ &:focus {
+ outline: none;
+ border-color: ${!isTouched
+ ? colors.gray02
+ : isValid === null
+ ? colors.gray02
+ : isValid
+ ? colors.primaryNormal
+ : colors.redNormal};
+ }
+`;
+
+const messageStyle = (isValid: boolean | null) => css`
+ margin-top: 6px;
+ color: ${isValid === null
+ ? colors.gray02
+ : isValid
+ ? colors.primaryNormal
+ : colors.redNormal};
+ font-size: ${fontSize.xs};
+`;
+export default InputBox;
diff --git a/src/components/KebabButton.tsx b/src/components/KebabButton.tsx
new file mode 100644
index 0000000..50432ab
--- /dev/null
+++ b/src/components/KebabButton.tsx
@@ -0,0 +1,83 @@
+import { useState, useRef } from 'react';
+import { css } from '@emotion/react';
+import { EllipsisVertical } from 'lucide-react';
+import IconButton from '@/components/IconButton';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import useOutsideClick from '@/hooks/useOutsideClick';
+
+interface KebabButtonProps {
+ menuItems: Array<{
+ label: string;
+ onClick: () => void;
+ }>;
+}
+
+const KebabButton: React.FC = ({ menuItems }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const menuRef = useRef(null);
+
+ const onMenuItemClick = (onClick: () => void) => {
+ onClick();
+ setIsOpen(false);
+ };
+
+ useOutsideClick(menuRef, () => {
+ if (isOpen) {
+ setIsOpen(false);
+ }
+ });
+
+ return (
+
+
{
+ setIsOpen(!isOpen);
+ }}
+ />
+ {isOpen && (
+
+ {menuItems.map((item, index) => (
+ - onMenuItemClick(item.onClick)}
+ >
+ {item.label}
+
+ ))}
+
+ )}
+
+ );
+};
+
+const kebabButtonStyle = css`
+ position: relative;
+ width: fit-content;
+`;
+
+const menuModalStyle = css`
+ position: absolute;
+ top: 30px;
+ right: 8px;
+ width: 124px;
+ background-color: ${colors.white};
+ border-radius: 4px;
+ z-index: 10;
+ box-shadow:
+ 0px 3px 8px 2px rgba(0, 0, 0, 0.12),
+ 0px 8px 10px 1px rgba(0, 0, 0, 0.14),
+ 0px 5px 5px -3px rgba(0, 0, 0, 0.2);
+
+ li {
+ font-size: ${fontSize.md};
+ padding: 8px 16px;
+ :hover {
+ background-color: ${colors.gray01};
+ }
+ }
+`;
+
+export default KebabButton;
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
new file mode 100644
index 0000000..9fd70fa
--- /dev/null
+++ b/src/components/Layout.tsx
@@ -0,0 +1,59 @@
+import { css } from '@emotion/react';
+import { Outlet, useLocation } from 'react-router-dom';
+import Header from '@/components/Header';
+import Navbar from '@/components/Navbar';
+import Toast from '@/components/Toast';
+import colors from '@/constants/colors';
+import { checkHeaderType } from '@/utils/headerUtils';
+
+export const Layout = () => {
+ const { pathname } = useLocation();
+ const headerType = checkHeaderType(pathname);
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+const headerNavbarHeight = '60px';
+
+const pageContainerStyles = css`
+ position: relative;
+ width: 100%;
+ max-width: 430px;
+ height: 100%;
+ margin: 0 auto;
+
+ main {
+ padding-top: ${headerNavbarHeight};
+ height: calc(100% - ${headerNavbarHeight});
+ }
+
+ &::before {
+ left: 50%;
+ transform: translateX(-215px);
+ }
+
+ &::after {
+ right: 50%;
+ transform: translateX(215px);
+ }
+
+ &::before,
+ &::after {
+ width: 1px;
+ position: fixed;
+ top: 0px;
+ bottom: 0px;
+ background-color: ${colors.gray02};
+ content: '';
+ z-index: 11;
+ }
+`;
diff --git a/src/components/LoadingSpinner.tsx b/src/components/LoadingSpinner.tsx
new file mode 100644
index 0000000..1d30255
--- /dev/null
+++ b/src/components/LoadingSpinner.tsx
@@ -0,0 +1,27 @@
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+
+const LoadingSpinner = () => ;
+
+const spinnerStyles = css`
+ border: 3px solid rgba(0, 0, 0, 0.1);
+ border-top: 3px solid ${colors.primaryLight};
+ border-radius: 50%;
+ min-width: 24px;
+ min-height: 24px;
+ max-width: 24px;
+ max-height: 24px;
+ animation: spin 1s linear infinite;
+ display: block;
+
+ @keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+ }
+`;
+
+export default LoadingSpinner;
diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx
new file mode 100644
index 0000000..dccaa54
--- /dev/null
+++ b/src/components/Logo.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import { useNavigate } from 'react-router-dom';
+import logo from '@/assets/logo.svg';
+import ROUTES from '@/constants/route';
+
+interface LogoProps {
+ logoWidth: number;
+ clickable?: boolean;
+}
+
+const Logo: React.FC = ({ logoWidth, clickable = false }) => {
+ const navigate = useNavigate();
+
+ const onLogoClick = (): void => {
+ navigate(ROUTES.ROOT);
+ };
+
+ return (
+
+ );
+};
+
+const LogoStyle = (logoWidth: number, clickable: boolean) => css`
+ width: ${logoWidth}px;
+ ${clickable && `cursor: pointer;`}
+`;
+
+export default Logo;
diff --git a/src/components/MiniPlaylist.tsx b/src/components/MiniPlaylist.tsx
new file mode 100644
index 0000000..588d12d
--- /dev/null
+++ b/src/components/MiniPlaylist.tsx
@@ -0,0 +1,125 @@
+import { Fragment } from 'react';
+import { css } from '@emotion/react';
+import { Transition } from '@headlessui/react';
+import { ChevronUp } from 'lucide-react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+
+interface VideoInfoProps {
+ playlistName: string;
+ subject: string;
+ thumbnail?: string;
+}
+
+interface MiniPlaylistProps {
+ isActive: boolean;
+ videoInfo: VideoInfoProps;
+ onClose: () => void;
+}
+
+const defaultImg = '/src/assets/defaultThumbnail.jpg';
+
+const MiniPlaylist = ({ isActive, videoInfo, onClose }: MiniPlaylistProps) => (
+
+
+

+
+
+ {videoInfo.subject}
+
+
+ {videoInfo.playlistName}
+
+
+
+
+
+
+
+);
+
+const navHeight = '60px';
+
+const toastStyle = css`
+ max-width: 430px;
+ min-width: 390px;
+ position: fixed;
+ margin: 0px 20px;
+ bottom: calc(${navHeight} + 20px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 10px 0;
+ border-radius: 8px;
+ background: rgba(101, 103, 109, 0.5);
+ backdrop-filter: blur(30px);
+ color: ${colors.white};
+ z-index: 1000;
+ transition: all 0.2s ease-in;
+ cursor: pointer;
+
+ &.transition-enter-from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+
+ &.transition-enter-to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ &.transition-leave-from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ &.transition-leave-to {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+`;
+
+const thumbnailStyle = css`
+ position: relative;
+ width: 80px;
+ height: 45px;
+ margin: 0 10px;
+`;
+
+const infoStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ font-size: ${fontSize.md};
+ flex-grow: 1;
+`;
+
+const buttonStyle = css`
+ padding: 0 10px;
+`;
+
+const messageStyle = css`
+ font-weight: 500;
+
+ &.subject {
+ color: ${colors.white};
+ font-size: ${fontSize.sm};
+ }
+ &.playlistName {
+ color: ${colors.gray02};
+ font-size: ${fontSize.sm};
+ font-weight: ${fontWeight.light};
+ }
+`;
+
+export default MiniPlaylist;
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
new file mode 100644
index 0000000..b20e3d9
--- /dev/null
+++ b/src/components/Modal.tsx
@@ -0,0 +1,100 @@
+import { css } from '@emotion/react';
+import Button from '@/components/Button';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import useModalStore from '@/stores/useModalStore';
+
+const Modal: React.FC = () => {
+ const { isOpen, modalData, closeModal } = useModalStore();
+
+ if (!isOpen || !modalData) return null;
+
+ const { type, title, content, onAction } = modalData;
+
+ return (
+
+
+
{title}
+
{content}
+
+
+
+
+ );
+};
+
+const modalBackgroundStyles = css`
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: rgba(0, 0, 0, 0.12);
+ z-index: 100;
+ .modal {
+ position: absolute;
+ flex-direction: column;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: ${colors.white};
+ display: flex;
+ border-radius: 8px;
+ width: 390px;
+ box-shadow:
+ 0px 6px 30px 5px rgba(0, 0, 0, 0.12),
+ 0px 16px 24px 2px rgba(0, 0, 0, 0.14),
+ 0px 8px 10px -5px rgba(0, 0, 0, 0.2);
+
+ h1 {
+ font-size: ${fontSize.lg};
+ padding: 16px 20px;
+ font-weight: ${fontWeight.semiBold};
+ }
+
+ h3 {
+ font-size: ${fontSize.md};
+ padding: 12px 20px 0px 20px;
+ text-align: center;
+ line-height: 27px;
+ }
+
+ .modal-buttons {
+ padding: 20px;
+ display: flex;
+ gap: 8px;
+ button {
+ width: 100%;
+ }
+ }
+ }
+`;
+
+export default Modal;
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
new file mode 100644
index 0000000..e3d312d
--- /dev/null
+++ b/src/components/Navbar.tsx
@@ -0,0 +1,86 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import { Home, Flame, Library, Users } from 'lucide-react';
+import { useNavigate, useLocation } from 'react-router-dom';
+import IconButton from '@/components/IconButton';
+import colors from '@/constants/colors';
+import ROUTES from '@/constants/route';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+interface NavList {
+ label: string;
+ icon: React.ComponentType;
+ to: string;
+}
+
+const navList: NavList[] = [
+ { label: 'ํ', icon: Home, to: ROUTES.ROOT },
+ { label: '์ธ๊ธฐ', icon: Flame, to: ROUTES.POPULAR },
+ { label: '๋ง์ดํ๋ฆฌ', icon: Library, to: ROUTES.PLAYLIST() },
+ { label: 'ํ๋ก์', icon: Users, to: ROUTES.FOLLOWING },
+];
+
+const Navbar: React.FC = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { userId } = useAuthStore();
+
+ return (
+
+ );
+};
+
+const navbarStyle = css`
+ width: 430px;
+ height: 60px;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ position: fixed;
+ bottom: 0;
+ padding: 10px 20px;
+ background-color: ${colors.white};
+ border-top: 1px solid ${colors.gray02};
+ z-index: 10;
+`;
+
+const iconWrapperStyle = css`
+ display: flex;
+ justify-content: center;
+ width: 60px;
+`;
+
+export default Navbar;
diff --git a/src/components/PlaylistCard.tsx b/src/components/PlaylistCard.tsx
new file mode 100644
index 0000000..8cf04ea
--- /dev/null
+++ b/src/components/PlaylistCard.tsx
@@ -0,0 +1,402 @@
+import React, { useEffect } from 'react';
+import { css } from '@emotion/react';
+import {
+ Heart,
+ MessageSquareMore,
+ LockKeyhole,
+ LockKeyholeOpen,
+ Bookmark,
+} from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import { deletePlaylist } from '@/api/playlistInfo';
+import IconButton from '@/components/IconButton';
+import KebabButton from '@/components/KebabButton';
+import Modal from '@/components/Modal';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import usePlaylistActions from '@/hooks/usePlaylistActions';
+import useToast from '@/hooks/useToast';
+import useModalStore from '@/stores/useModalStore';
+import { useVisibilityStore } from '@/stores/useVisibilityStore';
+import { PlayListDataProps } from '@/types/playlistType';
+import { omittedText } from '@/utils/textUtils';
+
+type CardSize = 'large' | 'small';
+
+interface PlaylistCardProps {
+ playlistItem: PlayListDataProps;
+ size: CardSize;
+ showAddButton?: boolean;
+ showLikeButton?: boolean;
+ showLockButton?: boolean;
+ showKebabMenu?: boolean;
+ onDelete?: (playlistId: number) => void;
+ isPreview?: boolean;
+}
+
+const MAXLENGTH = 50;
+
+const PlaylistCard: React.FC = ({
+ playlistItem,
+ size,
+ showAddButton = false,
+ showLikeButton = false,
+ showLockButton = false,
+ showKebabMenu = false,
+ onDelete,
+ isPreview = false,
+}) => {
+ const navigate = useNavigate();
+ const { openModal } = useModalStore();
+ const { visibilities, toggleVisibility, setInitialVisibility } =
+ useVisibilityStore();
+ const { isLiked, isAdded, toggleLike, toggleSave, likes } =
+ usePlaylistActions(
+ playlistItem.playlistId ? +playlistItem.playlistId : 0,
+ playlistItem.likes
+ );
+ const isPublic = playlistItem.playlistId
+ ? visibilities[playlistItem.playlistId]
+ : false;
+
+ const onCardClick = (): void => {
+ if (isPreview) return;
+ const restrictedRoot = /^\/playlist\/[^/]+\/add$/;
+
+ if (restrictedRoot.test(location.pathname)) {
+ return;
+ }
+
+ navigate(ROUTES.DETAIL(playlistItem.playlistId));
+ };
+
+ const onEditBtnClick = (): void => {
+ navigate(ROUTES.PLAYLIST_MODIFY(playlistItem.playlistId));
+ };
+
+ const onDeleteBtnClick = () => {
+ openModal({
+ type: 'delete',
+ title: 'ํ๋ ์ด๋ฆฌ์คํธ ์ญ์ ',
+ content: `'${playlistItem.title}' ์ ์ญ์ ํ์๊ฒ ์ต๋๊น?`,
+ onAction: async () => {
+ if (playlistItem.playlistId) {
+ const playlistIdNumber = Number(playlistItem.playlistId);
+ if (isNaN(playlistIdNumber)) {
+ toastTrigger('์ฌ๋ฐ๋ฅด์ง ์์ ํ๋ ์ด๋ฆฌ์คํธ ID์
๋๋ค.', 'fail');
+ return;
+ }
+ const response = await deletePlaylist(playlistIdNumber);
+ if (response.status === 'success') {
+ toastTrigger('ํ๋ ์ด๋ฆฌ์คํธ๊ฐ ์ญ์ ๋์์ต๋๋ค.', 'success');
+ if (onDelete) {
+ onDelete(playlistIdNumber);
+ }
+ } else {
+ toastTrigger('ํ๋ ์ด๋ฆฌ์คํธ ์ญ์ ์ ์คํจํ์ต๋๋ค.', 'fail');
+ }
+ } else {
+ toastTrigger('ํ๋ ์ด๋ฆฌ์คํธ ID๊ฐ ์์ต๋๋ค.', 'fail');
+ }
+ },
+ });
+ };
+
+ const { toastTrigger } = useToast();
+
+ const menuItems = [
+ {
+ label: '์์ ',
+ onClick: onEditBtnClick,
+ },
+ {
+ label: '์ญ์ ',
+ onClick: onDeleteBtnClick,
+ },
+ ];
+
+ useEffect(() => {
+ if (playlistItem.playlistId) {
+ setInitialVisibility(playlistItem.playlistId, playlistItem.isPublic);
+ }
+ }, [playlistItem.playlistId, playlistItem.isPublic, setInitialVisibility]);
+
+ const handleToggleVisibility = () => {
+ if (playlistItem.playlistId) {
+ toggleVisibility(playlistItem.playlistId);
+ }
+ };
+
+ const renderLargeCard = () => (
+
+
+
+
+
{
+ currentTarget.onerror = null; // prevents looping
+ currentTarget.src = '/src/assets/logoIcon.png';
+ }}
+ />
+
+
+
+
+ {omittedText(playlistItem.title, MAXLENGTH)}
+
+
+ {playlistItem.ownerChannelName} ยท ํธ๋ {playlistItem.links.length}๊ฐ
+
+
+ {playlistItem.tags.map((tag, index) => (
+ - {tag}
+ ))}
+
+
+
+ {
+ if (!!!isPreview) toggleLike();
+ }}
+ color={isLiked ? 'red' : 'gray'}
+ fillColor={isLiked ? 'red' : undefined}
+ />
+ {likes !== null ? likes : playlistItem.likes}
+
+
+
+ {playlistItem.commentCount}
+
+
+
+
+ );
+
+ const renderSmallCard = () => (
+
+
+
+
{
+ currentTarget.onerror = null; // prevents looping
+ currentTarget.src = '/src/assets/logoIcon.png';
+ }}
+ />
+
+
+
+ {omittedText(playlistItem.title, MAXLENGTH)}
+
+
+ {playlistItem.ownerChannelName} ยท ํธ๋ {playlistItem.links.length}๊ฐ
+ ยท ๋๊ธ {playlistItem.commentCount}๊ฐ
+
+
+ {playlistItem.tags.map((tag, index) => (
+ - {tag}
+ ))}
+
+
+
+
+ {showAddButton && (
+
+ )}
+ {showLikeButton && (
+
+ )}
+ {showLockButton && (
+
+ )}
+ {showKebabMenu && }
+
+
+
+ );
+
+ return <>{size === 'large' ? renderLargeCard() : renderSmallCard()}>;
+};
+
+const smallImgSize = '72px';
+
+const largeCardStyles = css`
+ gap: 10px;
+ flex-direction: column;
+ display: flex;
+`;
+
+const largeCardImgStyles = (isPreview = false) => css`
+ position: relative;
+ height: 210px;
+ max-width: 390px;
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+
+ span {
+ width: 90%;
+ height: 200px;
+ background-color: ${colors.gray04};
+ display: block;
+ border-radius: 12px;
+ }
+ .img-container {
+ position: absolute;
+ width: 100%;
+ top: 8px;
+ height: 200px;
+ border-radius: 10px;
+ border: 1px solid ${colors.white};
+ overflow: hidden;
+ ${!!!isPreview &&
+ css`
+ :hover {
+ cursor: pointer;
+ }
+ `}
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+`;
+
+const largeInfoStyles = (isPreview = false) => css`
+ gap: 6px;
+ flex-direction: column;
+ display: flex;
+ padding: 0 10px;
+ .title {
+ font-size: ${fontSize.md};
+ font-weight: ${fontWeight.medium};
+ ${!!!isPreview &&
+ css`
+ :hover {
+ cursor: pointer;
+ }
+ `}
+ }
+ .username {
+ color: ${colors.gray05};
+ font-size: ${fontSize.sm};
+ }
+ .tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ font-size: ${fontSize.md};
+ color: ${colors.primaryLight};
+ font-size: ${fontSize.sm};
+ }
+ .icons-container {
+ color: ${colors.gray05};
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ .icon {
+ display: flex;
+ align-items: center;
+ font-size: ${fontSize.sm};
+ gap: 2px;
+ button {
+ padding: 0;
+ }
+ svg {
+ width: 22px;
+ height: 22px;
+ }
+ }
+ }
+`;
+
+const smallContainerStyles = css`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 8px;
+`;
+
+const smallCardStyles = (isPreview = false) => css`
+ display: flex;
+ gap: 10px;
+
+ ${!!!isPreview &&
+ css`
+ :hover {
+ cursor: pointer;
+ }
+ `}
+
+ .img-container {
+ width: ${smallImgSize};
+ min-width: ${smallImgSize};
+ height: ${smallImgSize};
+ min-height: ${smallImgSize};
+ overflow: hidden;
+ border-radius: 6px;
+ display: flex;
+ justify-content: center;
+ background-color: ${colors.gray05};
+
+ img {
+ object-fit: cover;
+ }
+ }
+`;
+
+const smallInfoStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+
+ .title {
+ font-size: ${fontSize.md};
+ }
+ .username {
+ color: ${colors.gray05};
+ }
+ .tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ font-size: ${fontSize.sm};
+ color: ${colors.primaryLight};
+ }
+`;
+
+const smallBtnStyles = css`
+ display: flex;
+ color: ${colors.gray05};
+`;
+
+export default PlaylistCard;
diff --git a/src/components/PopupFilter.tsx b/src/components/PopupFilter.tsx
new file mode 100644
index 0000000..35e5293
--- /dev/null
+++ b/src/components/PopupFilter.tsx
@@ -0,0 +1,147 @@
+import React, { useRef, useState } from 'react';
+import { css } from '@emotion/react';
+import { ListFilter } from 'lucide-react';
+import colors from '@/constants/colors';
+import { fontWeight } from '@/constants/font';
+import useOutsideClick from '@/hooks/useOutsideClick';
+
+interface FilterOptionGroup {
+ label: string;
+ options: string[];
+}
+
+interface PopupFilterProps {
+ optionGroups: FilterOptionGroup[];
+ selectedIndexes: number[];
+ setSelectedIndexes: React.Dispatch>;
+}
+
+const PopupFilter: React.FC = ({
+ optionGroups,
+ selectedIndexes,
+ setSelectedIndexes,
+}) => {
+ const [filterActive, setFilterActive] = useState(false);
+ const popupRef = useRef(null);
+ const buttonRef = useRef(null);
+
+ useOutsideClick(popupRef, () => {
+ if (filterActive) {
+ setFilterActive(false);
+ }
+ });
+
+ const toggleFilterActive = () => setFilterActive((prev) => !prev);
+
+ const onFilterChange = (groupIdx: number, idx: number) => {
+ const updatedIndexes = [...selectedIndexes];
+ updatedIndexes[groupIdx] = idx;
+ setSelectedIndexes(updatedIndexes);
+ };
+
+ return (
+
+
+ ํํฐ
+
+
+ {optionGroups.map((group, groupIdx) => (
+
+
{group.label}
+
+ {group.options.map((option, idx) => (
+ onFilterChange(groupIdx, idx)}
+ >
+ {option}
+
+ ))}
+
+
+ ))}
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ position: relative;
+`;
+
+const filterButtonStyle = (filterActive: boolean) => css`
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ outline: none;
+ color: ${filterActive ? colors.white : colors.gray05};
+ border: 1px solid ${filterActive ? colors.primaryNormal : colors.gray03};
+ border-radius: 50px;
+ padding: 8px 12px;
+ gap: 6px;
+ background-color: ${filterActive ? colors.primaryNormal : 'transparent'};
+
+ &:hover {
+ cursor: pointer;
+ color: ${filterActive ? colors.white : colors.primaryLight};
+ border-color: ${filterActive ? 'none' : colors.primaryLight};
+ }
+`;
+
+const popupFilterStyle = (filterActive: boolean) => css`
+ display: ${filterActive ? 'inline-flex' : 'none'};
+ position: absolute;
+ justify-content: center;
+ flex-direction: column;
+ padding: 20px;
+ border-radius: 4px;
+ gap: 20px;
+ top: 45px;
+ box-shadow:
+ 0px 8px 10px -5px rgba(0, 0, 0, 0.2),
+ 0px 16px 24px 2px rgba(0, 0, 0, 0.14),
+ 0px 6px 30px 5px rgba(0, 0, 0, 0.12);
+ background-color: ${colors.white};
+ z-index: 1;
+
+ p {
+ font-weight: ${fontWeight.semiBold};
+ }
+`;
+
+const filterLabelStyle = css`
+ margin-bottom: 8px;
+`;
+
+const optionWrapperStyle = css`
+ display: flex;
+ gap: 4px;
+`;
+
+const optionStyle = (idx: number, selectedIndex: number) => css`
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ border: 1px solid
+ ${idx === selectedIndex ? colors.primaryLight : colors.gray02};
+ border-radius: 50px;
+ padding: 4px 12px;
+ white-space: nowrap;
+ color: ${idx === selectedIndex ? colors.primaryNormal : colors.gray05};
+ background-color: ${idx === selectedIndex
+ ? `${colors.primaryLight}1A`
+ : 'transparent'};
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
+
+export default PopupFilter;
diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx
new file mode 100644
index 0000000..714c662
--- /dev/null
+++ b/src/components/Profile.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { css } from '@emotion/react';
+
+type ProfileProps = {
+ src: string;
+ alt: string;
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+ onClick?: () => void;
+};
+
+const sizes = {
+ xs: 32,
+ sm: 36,
+ md: 48,
+ lg: 100,
+ xl: 120,
+};
+
+const Profile: React.FC = ({
+ src,
+ alt,
+ size = 'lg',
+ onClick = () => {},
+}) => (
+
+

+
+);
+
+export default Profile;
diff --git a/src/components/TabButton.tsx b/src/components/TabButton.tsx
new file mode 100644
index 0000000..1ef7e1d
--- /dev/null
+++ b/src/components/TabButton.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import { NavLink, useParams } from 'react-router-dom';
+import colors from '@/constants/colors';
+import { fontWeight } from '@/constants/font';
+
+interface TabButtonProps {
+ tabNames: string[];
+ tabLinks: ((userId?: string) => string)[];
+}
+
+const TabButton: React.FC = ({ tabNames, tabLinks }) => {
+ const { userId } = useParams<{ userId: string }>();
+
+ return (
+
+ {tabNames.map((name, index) => (
+ (isActive ? 'active' : '')}
+ end={index === 0}
+ >
+ {name}
+
+ ))}
+
+ );
+};
+
+const tabContainerStyle = css`
+ display: flex;
+ background-color: ${colors.white};
+ border-bottom: 1px solid ${colors.gray02};
+`;
+
+const tabLinkStyle = css`
+ position: relative;
+ padding: 12px 16px;
+ text-decoration: none;
+ color: ${colors.gray04};
+ font-weight: ${fontWeight.semiBold};
+
+ &:hover {
+ color: ${colors.black};
+ }
+
+ &.active {
+ color: ${colors.black};
+
+ &::after {
+ content: '';
+ position: absolute;
+ bottom: -1px;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ background-color: ${colors.primaryNormal};
+ }
+ }
+`;
+
+export default TabButton;
diff --git a/src/components/TextFilter.tsx b/src/components/TextFilter.tsx
new file mode 100644
index 0000000..9cfe605
--- /dev/null
+++ b/src/components/TextFilter.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+
+interface TextFilterProps {
+ options: string[];
+ selectedIndex: number;
+ setSelectedIndex: React.Dispatch>;
+}
+
+const TextFilter: React.FC = ({
+ options,
+ selectedIndex,
+ setSelectedIndex,
+}) => (
+
+ {options.map((option, idx) => (
+
+ setSelectedIndex(idx)}
+ >
+ {option}
+
+ {idx < options.length - 1 && |}
+
+ ))}
+
+);
+
+const textFilterStyle = css`
+ display: flex;
+ font-size: ${fontSize.sm};
+ font-weight: ${fontWeight.semiBold};
+`;
+
+const optionStyle = (idx: number, selectedIndex: number) => css`
+ color: ${idx === selectedIndex ? colors.primaryNormal : colors.black};
+
+ &:hover {
+ cursor: pointer;
+ color: ${colors.primaryNormal};
+ }
+`;
+
+const separatorStyle = css`
+ color: ${colors.gray03};
+ margin: 0 4px;
+`;
+
+export default TextFilter;
diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx
new file mode 100644
index 0000000..e6bac99
--- /dev/null
+++ b/src/components/Toast.tsx
@@ -0,0 +1,108 @@
+import { Fragment, useEffect } from 'react';
+import { css } from '@emotion/react';
+import { Transition } from '@headlessui/react';
+import { CircleCheck, CircleX } from 'lucide-react';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import useToast from '@/hooks/useToast';
+
+export type ToastStatusType = 'success' | 'fail';
+
+interface ToastProps {
+ duration?: number;
+}
+
+const Toast = ({ duration = 2000 }: ToastProps) => {
+ const { isToastOn, toastMsg, onClose, status } = useToast();
+
+ useEffect(() => {
+ if (isToastOn) {
+ const timer = setTimeout(onClose, duration);
+ return () => clearTimeout(timer);
+ }
+ }, [isToastOn, onClose, duration]);
+
+ return (
+
+
+
+
+ {status === 'success' ? (
+
+ ) : (
+
+ )}
+
+
{toastMsg}
+
+
+
+ );
+};
+
+const transStyle = css``;
+
+const toastStyle = css`
+ width: calc(100vw - 40px);
+ max-width: calc(430px - 40px);
+ position: fixed;
+ bottom: 60px;
+ display: flex;
+ align-items: center;
+ padding: 16px;
+ margin-left: 20px;
+ border-radius: 4px;
+ background-color: ${colors.gray04};
+ color: ${colors.white};
+ z-index: 1000;
+ transition: all 0.2s ease-in;
+
+ &.transition-enter-from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+
+ &.transition-enter-to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ &.transition-leave-from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ &.transition-leave-to {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+`;
+
+const iconContainerStyle = css`
+ position: relative;
+ border-radius: 50%;
+ margin-right: 0.5rem;
+ display: flex;
+`;
+
+const successStyle = css`
+ color: ${colors.greenLight};
+`;
+
+const failStyle = css`
+ color: ${colors.redNormal};
+`;
+
+const messageStyle = css`
+ font-size: ${fontSize.md};
+ font-weight: 500;
+`;
+
+export default Toast;
diff --git a/src/components/Toggle.tsx b/src/components/Toggle.tsx
new file mode 100644
index 0000000..6370e0b
--- /dev/null
+++ b/src/components/Toggle.tsx
@@ -0,0 +1,83 @@
+import { css } from '@emotion/react';
+import { Switch } from '@headlessui/react';
+import colors from '@/constants/colors';
+
+type ToggleLabel = {
+ active?: string;
+ inactive?: string;
+};
+
+interface ToggleProps {
+ enabled: boolean;
+ setEnabled: (value: boolean) => void;
+ label?: ToggleLabel;
+}
+
+const Toggle = ({ enabled, setEnabled, label }: ToggleProps) => {
+ const onToggle = () => setEnabled(!enabled);
+
+ return (
+
+ {label?.inactive && (
+
+ {label.inactive}
+
+ )}
+
+
+
+ {label?.active && (
+
+ {label.active}
+
+ )}
+
+ );
+};
+
+const toggleStyle = css`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+`;
+
+const switchStyle = (enabled: boolean) => css`
+ display: flex;
+ align-items: center;
+ width: 44px;
+ height: 24px;
+ padding: 2px;
+ border-radius: 12px;
+ background-color: ${enabled ? colors.primaryLight : colors.gray03};
+ transition: background-color 0.2s ease-in-out;
+ cursor: pointer;
+ border: none;
+`;
+
+const switchThumbStyle = (enabled: boolean) => css`
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background-color: ${colors.white};
+ transform: ${enabled ? 'translateX(20px)' : 'translateX(0)'};
+ transition: transform 0.2s ease-in-out;
+`;
+
+const labelStyle = (enabled: boolean) => css`
+ &.active {
+ padding-left: 5px;
+ color: ${enabled ? colors.black : colors.gray03};
+ }
+
+ &.inactive {
+ padding-right: 5px;
+ color: ${!!!enabled ? colors.black : colors.gray03};
+ }
+`;
+
+export default Toggle;
diff --git a/src/components/Video.tsx b/src/components/Video.tsx
new file mode 100644
index 0000000..2974e99
--- /dev/null
+++ b/src/components/Video.tsx
@@ -0,0 +1,88 @@
+import { css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import { omittedText } from '@/utils/textUtils';
+
+interface VideoProps {
+ imgUrl: string;
+ title: string;
+ userName: string;
+ provider: string;
+}
+
+const MAX_LENGTH = {
+ title: 50,
+ name: 20,
+};
+
+const ICON: Record = {
+ youtube: '/src/assets/youtube.png',
+ vimeo: '/src/assets/vimeo.png',
+ soundcloud: '/src/assets/soundCloud.png',
+ default: '/src/assets/logoIcon.png',
+};
+
+const Video = ({ imgUrl, title, userName, provider }: VideoProps) => (
+
+ <>>
+

+
+
{omittedText(title, MAX_LENGTH.title)}
+
{omittedText(userName, MAX_LENGTH.name)}
+ {provider && (
+
]})
{
+ currentTarget.onerror = null; // prevents looping
+ currentTarget.src = '/src/assets/logoIcon.png';
+ }}
+ />
+ )}
+
+
+);
+
+const VideoContainer = css`
+ display: flex;
+ cursor: pointer;
+`;
+
+const iconStyle = css`
+ width: 20px;
+`;
+
+const ThumbnailStyle = css`
+ width: 100px;
+ height: 75px;
+ max-width: 100px;
+ max-height: 75px;
+ min-width: 100px;
+ min-height: 75px;
+ border-radius: 6px;
+`;
+
+const VideoInfoStyle = css`
+ display: flex;
+ flex-direction: column;
+ padding: 5px 10px;
+ justify-content: space-around;
+`;
+const TitleStyle = css`
+ font-size: ${fontSize.sm};
+ font-weight: ${fontWeight.medium};
+ color: ${colors.black};
+ overflow: hidden;
+ text-overflow: ellipsis;
+ word-break: break-all;
+`;
+const UserNameStyle = css`
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+
+ &:hover {
+ text-decoration: underline;
+ }
+`;
+
+export default Video;
diff --git a/src/components/playlist/AddedVideo.tsx b/src/components/playlist/AddedVideo.tsx
new file mode 100644
index 0000000..ca443ce
--- /dev/null
+++ b/src/components/playlist/AddedVideo.tsx
@@ -0,0 +1,103 @@
+import React from 'react';
+import { css } from '@emotion/react';
+import { AlignJustify, X } from 'lucide-react';
+import IconButton from '@/components/IconButton';
+import Video from '@/components/Video';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import { VideoProps } from '@/types/video';
+import { omittedText } from '@/utils/textUtils';
+
+export interface AddedLinkProps extends VideoProps {
+ videoId: string;
+ isLinkView?: boolean;
+ link: string;
+ isRemovable?: boolean;
+ onRemove?: (videoId: string) => void;
+ isDragNDrop?: boolean;
+ onDragNDrop?: () => void;
+ onClick?: () => void;
+ isActive?: boolean;
+ embedCode?: string;
+ provider?: string;
+}
+
+const AddedVideo: React.FC = ({
+ videoId,
+ imgUrl,
+ isRemovable = true,
+ onRemove,
+ isLinkView = true,
+ link,
+ title,
+ userName,
+ isDragNDrop = false,
+ onDragNDrop,
+ onClick = () => {},
+ isActive = false,
+ provider = 'default',
+}) => (
+
+ {isDragNDrop && onDragNDrop !== undefined && (
+
+ )}
+
+
+ {isLinkView &&
{omittedText(link, 40)}
}
+ {isRemovable && onRemove && (
+
{
+ onRemove(videoId);
+ }}
+ />
+ )}
+
+
+
+
+);
+
+const videoItemStyle = (isActive: boolean) => css`
+ display: flex;
+ width: calc(100vw - 40px);
+ max-width: 100%;
+ padding: 6px;
+ border-left: 4px solid rgba(255, 255, 255, 0);
+
+ ${isActive &&
+ css`
+ background: rgba(63, 132, 243, 0.05);
+ border-left: 4px solid ${colors.primaryLight};
+ `}
+`;
+
+const videoLinkStyle = css`
+ color: ${colors.primaryNormal};
+ display: flex;
+ align-items: center;
+
+ & p {
+ flex-grow: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ font-size: ${fontSize.sm};
+ padding: 10px 0;
+ }
+`;
+
+const linkInfoStyle = css`
+ flex-grow: 1;
+`;
+
+export default AddedVideo;
diff --git a/src/constants/colors.ts b/src/constants/colors.ts
new file mode 100644
index 0000000..44dc773
--- /dev/null
+++ b/src/constants/colors.ts
@@ -0,0 +1,21 @@
+const colors = {
+ primaryLight: '#3F84F3',
+ primaryNormal: '#3366FF',
+ primaryDark: '#0C51C0',
+ redLight: '#F9837C',
+ redNormal: '#F65349',
+ redDark: '#F4271B',
+ greenLight: '#32DCBD',
+ greenNormal: '#20BA9E',
+ greenDark: '#19907A',
+ gray01: '#F5F5F5',
+ gray02: '#E6E7E7',
+ gray03: '#BCBDBF',
+ gray04: '#A5A5A5',
+ gray05: '#737373',
+ gray06: '#545454',
+ black: '#2C2C2C',
+ white: '#FFFFFF',
+};
+
+export default colors;
diff --git a/src/constants/font.ts b/src/constants/font.ts
new file mode 100644
index 0000000..6b65a77
--- /dev/null
+++ b/src/constants/font.ts
@@ -0,0 +1,22 @@
+export const fontSize = {
+ xxxl: '2.8rem',
+ xxl: '2.4rem',
+ xl: '2rem',
+ lg: '1.8rem',
+ md: '1.6rem',
+ sm: '1.4rem',
+ xs: '1.2rem',
+ xxs: '1.1rem',
+};
+
+export const fontWeight = {
+ thin: 100,
+ extraLight: 200,
+ light: 300,
+ regular: 400,
+ medium: 500,
+ semiBold: 600,
+ bold: 700,
+ extraBold: 800,
+ black: 900,
+};
diff --git a/src/constants/hashtag.ts b/src/constants/hashtag.ts
new file mode 100644
index 0000000..286ddc4
--- /dev/null
+++ b/src/constants/hashtag.ts
@@ -0,0 +1,29 @@
+import { Tag } from '@/components/HashTag';
+
+export const HASHTAGS: Tag[] = [
+ { id: 1, label: '#๋จน๋ฐฉ', removable: false },
+ { id: 2, label: '#Vlog', removable: false },
+ { id: 3, label: '#Beauty', removable: false },
+ { id: 4, label: '#Dogs', removable: false },
+ { id: 5, label: '#BackEnd', removable: false },
+ { id: 6, label: '#FrontEnd', removable: false },
+ { id: 7, label: '#Game', removable: false },
+ { id: 8, label: '#Music', removable: false },
+ { id: 9, label: '#์ฌํ', removable: false },
+ { id: 10, label: '#์์ค', removable: false },
+ { id: 11, label: '#Health', removable: false },
+ { id: 12, label: '#์ฃผ์', removable: false },
+ { id: 13, label: '#OOTD', removable: false },
+ { id: 14, label: '#๋ฐ์ผ๋ฆฌ๋ฃฉ', removable: false },
+ { id: 15, label: '#photography', removable: false },
+ { id: 16, label: '#ํธ๋๋ฉ์ด๋', removable: false },
+ { id: 17, label: '#Design', removable: false },
+ { id: 18, label: '#์ธํ
๋ฆฌ์ด', removable: false },
+ { id: 19, label: '#๋ง์ง', removable: false },
+ { id: 20, label: '#smallbusiness', removable: false },
+ { id: 21, label: '#๋๊ธฐ๋ถ์ฌ', removable: false },
+ { id: 22, label: '#์๊ธฐ๊ณ๋ฐ', removable: false },
+ { id: 23, label: '#๋ฐ๋ ', removable: false },
+ { id: 24, label: '#Fashion', removable: false },
+ { id: 25, label: '#๋๋ผ๋ง', removable: false },
+];
diff --git a/src/constants/playlist.ts b/src/constants/playlist.ts
new file mode 100644
index 0000000..0e1ac58
--- /dev/null
+++ b/src/constants/playlist.ts
@@ -0,0 +1,41 @@
+export const TEXT = {
+ toggle: { active: '๊ณต๊ฐ', inactive: '๋น๊ณต๊ฐ' },
+ title: {
+ label: 'ํ๋ ์ด๋ฆฌ์คํธ ์ ๋ชฉ',
+ placeholder: 'ํ๋ ์ด๋ฆฌ์คํธ ์ ๋ชฉ์ ์
๋ ฅํด์ฃผ์ธ์.',
+ required: '์ ๋ชฉ์ ์
๋ ฅํด์ฃผ์ธ์.',
+ },
+ desc: {
+ label: 'ํ๋ ์ด๋ฆฌ์คํธ ์ค๋ช
',
+ placeholder: 'ํ๋ ์ด๋ฆฌ์คํธ์ ๋ํ ์ค๋ช
์ ๋จ๊ฒจ์ฃผ์ธ์.',
+ },
+ link: {
+ label: '์์ ๋งํฌ ์ถ๊ฐ',
+ placeholder: '์์ ๋งํฌ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.',
+ validmsg: '์ง์ํ์ง ์๋ ์์์
๋๋ค.',
+ validDuplmsg: '์ค๋ณต๋ ๋งํฌ์
๋๋ค.',
+ required: '์์๋งํฌ๋ฅผ 1๊ฐ์ด์ ์
๋ ฅํด์ฃผ์ธ์.',
+ },
+ hashtag: {
+ label: 'ํด์ํ๊ทธ',
+ desc: '์ต๋ 5๊ฐ๊น์ง ์
๋ ฅ ๊ฐ๋ฅํฉ๋๋ค.',
+ placeholder: '#',
+ required: 'ํด์ํ๊ทธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์',
+ limit: 'ํด์ํ๊ทธ๋ 5๊ฐ๊น์ง ๋ฑ๋กํ ์ ์์ต๋๋ค.',
+ validDuplmsg: '์ค๋ณต๋ ํด์ํ๊ทธ์
๋๋ค.',
+ },
+ thumbnail: {
+ label: '์ธ๋ค์ผ',
+ desc: 'ํ๋ ์ด๋ฆฌ์คํธ ๋ฉ์ธ ์ธ๋ค์ผ์ ์ ํํด์ฃผ์ธ์.',
+ },
+ preview: {
+ large: '๋ฏธ๋ฆฌ๋ณด๊ธฐ (large)',
+ small: '๋ฏธ๋ฆฌ๋ณด๊ธฐ (small)',
+ },
+ createButton: { label: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ฑํ๊ธฐ', loading: '์์ฑ ์ค...' },
+ modifyButton: { label: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ ํ๊ธฐ', loading: '์์ ์ค...' },
+ toast: {
+ create: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ์ด ์๋ฃ๋์์ต๋๋ค.',
+ modify: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ ์ด ์๋ฃ๋์์ต๋๋ค.',
+ },
+};
diff --git a/src/constants/route.ts b/src/constants/route.ts
new file mode 100644
index 0000000..a9731a1
--- /dev/null
+++ b/src/constants/route.ts
@@ -0,0 +1,27 @@
+// src/constants/routes.js
+
+const ROUTES = {
+ ROOT: '/',
+ SIGN_IN: '/signin',
+ SIGN_UP: '/signup',
+ HASH_TAG: '/hashtag',
+ POPULAR: '/popular',
+ FOLLOWING: '/following',
+ // FOLLOWING_LIST: (userId = ':userId') => `/following/${userId}`,
+ FOLLOWING_LIST: (userId = ':userId') => `/profile/${userId}/following`,
+ DETAIL: (playListId = ':playlistId') => `/detail/${playListId}`,
+ SEARCH: (keyword = ':keyword') => `/search/${keyword}`,
+ PLAYLIST: (userId = ':userId') => `/playlist/${userId}`,
+ PLAYLIST_SAVED: (userId = ':userId') => `/playlist/${userId}/saved`,
+ PLAYLIST_LIKES: (userId = ':userId') => `/playlist/${userId}/likes`,
+ PLAYLIST_ADD: (userId = ':userId') => `/playlist/${userId}/add`,
+ PLAYLIST_MODIFY: (playlistId = ':playlistId') =>
+ `/playlist/${playlistId}/modify`,
+ PROFILE: (userId = ':userId') => `/profile/${userId}`,
+ PROFILE_MODIFY: (userId = ':userId') => `/profile/${userId}/modify`,
+ PROFILE_FOLLOWER: (userId = ':userId') => `/profile/${userId}/follower`,
+ PROFILE_FOLLOWING: (userId = ':userId') => `/profile/${userId}/following`,
+ NOT_FOUND: '*',
+};
+
+export default ROUTES;
diff --git a/src/constants/validation.ts b/src/constants/validation.ts
new file mode 100644
index 0000000..da9a1af
--- /dev/null
+++ b/src/constants/validation.ts
@@ -0,0 +1,7 @@
+export const Regex: Record = {
+ youtube:
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([A-Za-z0-9_-]+)/i,
+ vimeo: /vimeo\.com(?:\/channels\/\w+)?\/(\d+)$/i,
+ soundcloud:
+ /(?:soundcloud\.com|on\.soundcloud\.com|soundcloud\.app\.goog\.gl)\/(.*[\/].*)/,
+};
diff --git a/src/env.d.ts b/src/env.d.ts
new file mode 100644
index 0000000..8b37fc7
--- /dev/null
+++ b/src/env.d.ts
@@ -0,0 +1,15 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_FIREBASE_API_KEY: string;
+ readonly VITE_FIREBASE_AUTH_DOMAIN: string;
+ readonly VITE_FIREBASE_PROJECT_ID: string;
+ readonly VITE_FIREBASE_STORAGE_BUCKET: string;
+ readonly VITE_FIREBASE_MESSAGING_SENDER_ID: string;
+ readonly VITE_FIREBASE_APP_ID: string;
+ readonly VITE_FIREBASE_MEASUREMENT_ID: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}
diff --git a/src/firebase/firbaseConfig.ts b/src/firebase/firbaseConfig.ts
new file mode 100644
index 0000000..78db56a
--- /dev/null
+++ b/src/firebase/firbaseConfig.ts
@@ -0,0 +1,32 @@
+// Import the functions you need from the SDKs you need
+import { initializeApp } from 'firebase/app';
+import { getAuth } from 'firebase/auth';
+import { getFirestore } from 'firebase/firestore';
+import { getStorage } from 'firebase/storage';
+
+const firebaseConfig = {
+ apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
+ authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
+ projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
+ storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
+ messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
+ appId: import.meta.env.VITE_FIREBASE_APP_ID,
+ measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
+};
+
+// Initialize Firebase
+const app = initializeApp(firebaseConfig);
+export const db = getFirestore(app);
+export const storage = getStorage(app);
+export const auth = getAuth(app);
+
+export const COLLECTION = {
+ users: 'users',
+ playlist: 'playlist',
+ comments: 'comments',
+};
+
+export const STORAGE = {
+ profile: 'profile',
+ playlist: 'playlist',
+};
diff --git a/src/hooks/useChannel.ts b/src/hooks/useChannel.ts
new file mode 100644
index 0000000..d7ea2a9
--- /dev/null
+++ b/src/hooks/useChannel.ts
@@ -0,0 +1,62 @@
+import { useState, useEffect } from 'react';
+import { collection, query, where, doc, onSnapshot } from 'firebase/firestore';
+import { db } from '@/firebase/firbaseConfig';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+interface Channel {
+ id: string;
+ profileImg: string;
+ channelName: string;
+}
+
+type ListType = 'following' | 'follower';
+
+const useChannel = (channelId: string, listType: ListType) => {
+ const [channels, setChannels] = useState([]);
+ const { userId } = useAuthStore();
+
+ useEffect(() => {
+ const userDocRef = doc(db, 'users', channelId);
+
+ const unsubscribe = onSnapshot(userDocRef, (doc) => {
+ if (doc.exists()) {
+ const userData = doc.data() as {
+ channelFollowing: string[];
+ channelFollower: string[];
+ };
+
+ const listToFetch =
+ listType === 'following'
+ ? userData.channelFollowing
+ : userData.channelFollower;
+
+ if (listToFetch && listToFetch.length > 0) {
+ const usersQuery = query(
+ collection(db, 'users'),
+ where('__name__', 'in', listToFetch)
+ );
+
+ const unsubscribeUsers = onSnapshot(usersQuery, (querySnapshot) => {
+ const fetchedChannels = querySnapshot.docs.map((doc) => ({
+ id: doc.id,
+ profileImg: doc.data().profileImg || '',
+ channelName: doc.data().channelName || '',
+ }));
+ setChannels(fetchedChannels);
+ });
+
+ return () => unsubscribeUsers();
+ } else {
+ setChannels([]);
+ }
+ } else {
+ setChannels([]);
+ }
+ });
+
+ return () => unsubscribe();
+ }, [channelId, listType, userId]);
+ return channels;
+};
+
+export default useChannel;
diff --git a/src/hooks/useCheckDuplicate.ts b/src/hooks/useCheckDuplicate.ts
new file mode 100644
index 0000000..e980e59
--- /dev/null
+++ b/src/hooks/useCheckDuplicate.ts
@@ -0,0 +1,30 @@
+import { useState } from 'react';
+
+export const useCheckDuplicate = () => {
+ const [message, setMessage] = useState('');
+ const [isChecked, setIsChecked] = useState(false);
+
+ const checkDuplicate = async (
+ value: string,
+ checkFunc: (val: string) => Promise,
+ errorMessage: string,
+ successMessage: string
+ ) => {
+ if (!value) {
+ setMessage('');
+ setIsChecked(false);
+ return;
+ }
+
+ const exists = await checkFunc(value);
+ if (exists) {
+ setMessage(errorMessage);
+ setIsChecked(false);
+ } else {
+ setMessage(successMessage);
+ setIsChecked(true);
+ }
+ };
+
+ return { message, isChecked, checkDuplicate, setMessage, setIsChecked };
+};
diff --git a/src/hooks/useDetailForm.ts b/src/hooks/useDetailForm.ts
new file mode 100644
index 0000000..b33ec8c
--- /dev/null
+++ b/src/hooks/useDetailForm.ts
@@ -0,0 +1,91 @@
+import { useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import { addComment } from '@/api/comment';
+import ROUTES from '@/constants/route';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { CommentProps } from '@/types/playlistType';
+
+const useDetailForm = () => {
+ const { playlistId } = useParams();
+ const { userId } = useAuthStore();
+ const { toastTrigger } = useToast();
+ const navigate = useNavigate();
+
+ interface VALUES {
+ comment: string;
+ currentVideoIndex: number;
+ }
+
+ const INIT_VALUES: VALUES = {
+ comment: '',
+ currentVideoIndex: 0,
+ };
+
+ const [values, setValues] = useState(INIT_VALUES);
+ const onChanges = {
+ comment: (e: React.ChangeEvent) => {
+ setValues({ ...values, comment: e.target.value });
+ },
+ };
+ const onKeydowns = {
+ comment: (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') onClicks.comment();
+ },
+ };
+ const onClicks = {
+ comment: async () => {
+ if (!!!validations.comment()) return;
+ const commentData: CommentProps = {
+ playlistId: Number(playlistId),
+ content: values.comment,
+ isEdited: false,
+ regDate: new Date().toISOString(),
+ userId,
+ };
+
+ const { status } = await addComment(commentData);
+ if (status === 'success') {
+ setValues({ ...values, comment: '' });
+ toastTrigger('๋๊ธ์ด ๋ฑ๋ก๋์์ต๋๋ค.', 'success');
+ } else {
+ toastTrigger('๋๊ธ๋ฑ๋ก์ด ์คํจํ์ต๋๋ค.', 'fail');
+ }
+ },
+ copy: () => {
+ navigator.clipboard.writeText(window.location.href);
+ toastTrigger('๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค.', 'success');
+ },
+ video: (index: number) => {
+ setValues({ ...values, currentVideoIndex: index });
+ },
+ profile: (userId: string) => {
+ navigate(ROUTES.PLAYLIST(userId));
+ },
+ };
+
+ const validations = {
+ comment: (): boolean => {
+ if (isNaN(Number(playlistId))) return false;
+ if (!!!userId || userId.trim() === '') {
+ toastTrigger('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.', 'fail');
+ return false;
+ }
+ if (values.comment.trim().length < 1) {
+ toastTrigger('๋๊ธ์ ์
๋ ฅํด์ฃผ์ธ์', 'fail');
+ return false;
+ }
+ return true;
+ },
+ };
+
+ return {
+ values,
+ onChanges,
+ onKeydowns,
+ onClicks,
+ validations,
+ };
+};
+
+export default useDetailForm;
diff --git a/src/hooks/useFollowToggle.ts b/src/hooks/useFollowToggle.ts
new file mode 100644
index 0000000..c1986ac
--- /dev/null
+++ b/src/hooks/useFollowToggle.ts
@@ -0,0 +1,71 @@
+import { useCallback, useState, useEffect } from 'react';
+import {
+ doc,
+ updateDoc,
+ arrayUnion,
+ arrayRemove,
+ getDoc,
+} from 'firebase/firestore';
+import { db } from '@/firebase/firbaseConfig';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+export const useFollowToggle = (channelUserId: string | undefined) => {
+ const { userId: currentUserId, fetchUserData } = useAuthStore();
+ const [isFollowing, setIsFollowing] = useState(false);
+
+ useEffect(() => {
+ const checkFollowStatus = async () => {
+ if (!channelUserId || !currentUserId) return;
+
+ const currentUserRef = doc(db, 'users', currentUserId);
+ const currentUserDoc = await getDoc(currentUserRef);
+
+ if (currentUserDoc.exists()) {
+ const userData = currentUserDoc.data();
+ setIsFollowing(
+ userData.channelFollowing?.includes(channelUserId) || false
+ );
+ }
+ };
+
+ checkFollowStatus();
+ }, [channelUserId, currentUserId]);
+
+ const handleFollowToggle = useCallback(async () => {
+ if (!channelUserId || !currentUserId || channelUserId === currentUserId)
+ return;
+
+ const channelRef = doc(db, 'users', channelUserId);
+ const currentUserRef = doc(db, 'users', currentUserId);
+
+ try {
+ if (isFollowing) {
+ // ํ๋ก์ฐ ์ทจ์
+ await updateDoc(channelRef, {
+ channelFollower: arrayRemove(currentUserId),
+ });
+ await updateDoc(currentUserRef, {
+ channelFollowing: arrayRemove(channelUserId),
+ });
+ } else {
+ // ํ๋ก์ฐ
+ await updateDoc(channelRef, {
+ channelFollower: arrayUnion(currentUserId),
+ });
+ await updateDoc(currentUserRef, {
+ channelFollowing: arrayUnion(channelUserId),
+ });
+ }
+
+ setIsFollowing(!isFollowing);
+ await fetchUserData(currentUserId);
+ } catch (error) {
+ console.error(
+ 'ํ๋ก์ฐ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ๋์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค:',
+ error
+ );
+ }
+ }, [isFollowing, channelUserId, currentUserId, fetchUserData]);
+
+ return { isFollowing, handleFollowToggle };
+};
diff --git a/src/hooks/useFollowingPlaylist.ts b/src/hooks/useFollowingPlaylist.ts
new file mode 100644
index 0000000..fcbd1d3
--- /dev/null
+++ b/src/hooks/useFollowingPlaylist.ts
@@ -0,0 +1,18 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+import fetchFollowingPlaylists from '@/api/followingPlaylists';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+const useFollowingPlaylistFetch = (userId: string) => {
+ const loginUserId = useAuthStore().userId;
+
+ return useInfiniteQuery({
+ queryKey: ['followingPlaylists', userId],
+ queryFn: ({ pageParam = 0 }) =>
+ fetchFollowingPlaylists(userId, loginUserId, 5, pageParam),
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ initialPageParam: 0,
+ enabled: !!userId,
+ });
+};
+
+export default useFollowingPlaylistFetch;
diff --git a/src/hooks/useGenerateTags .ts b/src/hooks/useGenerateTags .ts
new file mode 100644
index 0000000..5a51df2
--- /dev/null
+++ b/src/hooks/useGenerateTags .ts
@@ -0,0 +1,30 @@
+import { useState, useEffect } from 'react';
+import { HASHTAGS } from '@/constants/hashtag';
+import { useAuthStore } from '@/stores/useAuthStore';
+import randomTags from '@/utils/randomTags';
+
+const useGenerateTags = (fixedTag: string) => {
+ const [tags, setTags] = useState([fixedTag]);
+ const { tags: userTags, userId } = useAuthStore();
+
+ useEffect(() => {
+ const neededTagsCount = 3 - userTags.length;
+ let additionalTags: string[] = [];
+
+ if (neededTagsCount > 0) {
+ const availableTags = HASHTAGS.map((tag) => tag.label).filter(
+ (tag) => !userTags.includes(tag)
+ );
+ additionalTags = randomTags(availableTags, neededTagsCount);
+ }
+
+ const allTags = [...userTags, ...additionalTags].slice(0, 3);
+ const selectedTags = randomTags(allTags, 3);
+
+ setTags([fixedTag, ...selectedTags]);
+ }, [fixedTag, userTags, userId]);
+
+ return tags;
+};
+
+export default useGenerateTags;
diff --git a/src/hooks/useHashtagManage.ts b/src/hooks/useHashtagManage.ts
new file mode 100644
index 0000000..08f6fcf
--- /dev/null
+++ b/src/hooks/useHashtagManage.ts
@@ -0,0 +1,55 @@
+import { useState } from 'react';
+import useToast from '@/hooks/useToast';
+
+export const useHashtagManage = () => {
+ const [hashtags, setHashtags] = useState([]);
+ const [hashtag, setHashtag] = useState('');
+ const { toastTrigger } = useToast();
+
+ const addHashtag = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+
+ const invalidString = /[\s!@#$%^&*(),.?":{}|<>]/;
+ if (invalidString.test(hashtag)) {
+ toastTrigger(
+ 'ํด์ํ๊ทธ์ ๊ณต๋ฐฑ์ด๋ ํน์๋ฌธ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.',
+ 'fail'
+ );
+ return;
+ }
+
+ if (hashtags.length >= 10) {
+ toastTrigger('ํด์ํ๊ทธ๋ ์ต๋ 10๊ฐ ์ ํ์
๋๋ค.', 'fail');
+ return;
+ }
+
+ const formattedHashtag = hashtag.startsWith('#')
+ ? hashtag
+ : `#${hashtag}`;
+
+ if (hashtag && hashtags.includes(formattedHashtag)) {
+ toastTrigger('์ด๋ฏธ ์๋ ํด์ํ๊ทธ์
๋๋ค.', 'fail');
+ return;
+ }
+
+ if (hashtag) {
+ setHashtags((prev) => [...prev, formattedHashtag]);
+ setHashtag('');
+ }
+ }
+ };
+
+ const removeHashtag = (label: string) => {
+ setHashtags((prevHashtags) => prevHashtags.filter((tag) => tag !== label));
+ };
+
+ return {
+ hashtags,
+ hashtag,
+ setHashtag,
+ setHashtags,
+ addHashtag,
+ removeHashtag,
+ };
+};
diff --git a/src/hooks/useHomePlaylist.ts b/src/hooks/useHomePlaylist.ts
new file mode 100644
index 0000000..f2c9d59
--- /dev/null
+++ b/src/hooks/useHomePlaylist.ts
@@ -0,0 +1,11 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+import { fetchHomePlaylists } from '@/api/homePlaylists';
+
+export const useFetchHomePlaylists = (sortOption = '์ต์ ์', pageSize = 5) =>
+ useInfiniteQuery({
+ queryKey: ['homePlaylists', sortOption],
+ queryFn: ({ pageParam = 0 }) =>
+ fetchHomePlaylists(pageParam, sortOption, pageSize),
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ initialPageParam: 0,
+ });
diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts
new file mode 100644
index 0000000..857ba61
--- /dev/null
+++ b/src/hooks/useInfiniteScroll.ts
@@ -0,0 +1,38 @@
+import { useCallback, useEffect, useRef } from 'react';
+
+interface IntersectionObserverInit {
+ root?: Element | Document | null;
+ rootMargin?: string;
+ threshold?: number;
+}
+
+type IntersectHandler = (
+ entry: IntersectionObserverEntry,
+ observer: IntersectionObserver
+) => void;
+
+const useInfiniteScroll = (
+ onIntersect: IntersectHandler,
+ options?: IntersectionObserverInit
+) => {
+ const ref = useRef(null);
+ const callback = useCallback(
+ (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) onIntersect(entry, observer);
+ });
+ },
+ [onIntersect]
+ );
+
+ useEffect(() => {
+ if (!ref.current) return;
+ const observer = new IntersectionObserver(callback, options);
+ observer.observe(ref.current);
+ return () => observer.disconnect();
+ }, [ref, options, callback]);
+
+ return ref;
+};
+
+export default useInfiniteScroll;
diff --git a/src/hooks/useList.ts b/src/hooks/useList.ts
new file mode 100644
index 0000000..4a312b6
--- /dev/null
+++ b/src/hooks/useList.ts
@@ -0,0 +1,66 @@
+import { useState, useEffect } from 'react';
+import useChannel from '@/hooks/useChannel';
+import { useAuthStore } from '@/stores/useAuthStore';
+import useModalStore from '@/stores/useModalStore';
+
+interface ChannelProps {
+ src: string;
+ alt: 'Profile Image';
+ size?: 'sm';
+ name: string;
+ channelName: string;
+}
+
+type ListType = 'following' | 'follower';
+
+const useList = (userId: string, listType: ListType) => {
+ const [list, setList] = useState([]);
+ const { openModal } = useModalStore();
+ const channels = useChannel(userId, listType);
+ const { removeFollowing, removeFollower } = useAuthStore.getState();
+
+ useEffect(() => {
+ setList(
+ channels.map((channel) => ({
+ src: channel.profileImg || '',
+ alt: 'Profile Image',
+ size: 'sm',
+ name: channel.id,
+ channelName: channel.channelName,
+ }))
+ );
+ }, [channels]);
+
+ const handleRemove = async (uid: string) => {
+ try {
+ if (listType === 'following') {
+ await removeFollowing(userId, uid);
+ } else if (listType === 'follower') {
+ await removeFollower(userId, uid);
+ }
+ setList((prevList) => prevList.filter((user) => user.name !== uid));
+ } catch (error) {
+ console.error(`Error removing ${listType}:`, error);
+ }
+ };
+
+ const handleUserMinusClick = (uid: string) => {
+ openModal({
+ type: 'confirm',
+ title: listType === 'following' ? '์ธํ๋ก์ฐ ํ์ธ' : 'ํ๋ก์ ์ญ์ ํ์ธ',
+ content:
+ listType === 'following'
+ ? '์ ๋ง๋ก ์ด ์ ์ ๋ฅผ ์ธํ๋ก์ฐ ํ์๊ฒ ์ต๋๊น?'
+ : '์ ๋ง๋ก ์ด ์ ์ ๋ฅผ ํ๋ก์์์ ์ญ์ ํ์๊ฒ ์ต๋๊น?',
+ onAction: () => handleRemove(uid),
+ });
+ };
+
+ return {
+ list,
+ handleUserMinusClick,
+ setList,
+ };
+};
+
+export default useList;
diff --git a/src/hooks/useOutsideClick.ts b/src/hooks/useOutsideClick.ts
new file mode 100644
index 0000000..2834211
--- /dev/null
+++ b/src/hooks/useOutsideClick.ts
@@ -0,0 +1,24 @@
+import { useEffect, RefObject } from 'react';
+
+type Handler = () => void;
+
+const useOutsideClick = (
+ ref: RefObject,
+ handler: Handler
+): void => {
+ useEffect(() => {
+ const listener = (event: MouseEvent) => {
+ if (!ref.current || ref.current.contains(event.target as Node)) {
+ return;
+ }
+ handler();
+ };
+
+ document.addEventListener('mousedown', listener);
+ return () => {
+ document.removeEventListener('mousedown', listener);
+ };
+ }, [ref, handler]);
+};
+
+export default useOutsideClick;
diff --git a/src/hooks/usePlaylistActions.ts b/src/hooks/usePlaylistActions.ts
new file mode 100644
index 0000000..2a7cb5c
--- /dev/null
+++ b/src/hooks/usePlaylistActions.ts
@@ -0,0 +1,147 @@
+import { useState } from 'react';
+import { useQueryClient, QueryClient } from '@tanstack/react-query';
+import { useLocation } from 'react-router-dom';
+import { useUpdateUserPlaylists } from '@/api/playlistActions';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { PlayListDataProps } from '@/types/playlistType';
+
+type QueryDataType = {
+ pages: {
+ playlistsData: PlayListDataProps[];
+ }[];
+};
+
+const PAGES_QUERY: { [key: string]: string } = {
+ '': 'homePlaylists',
+ search: 'searchPlaylists',
+ popular: 'popularPlaylists',
+ following: 'followingPlaylists',
+};
+
+const updatePlaylistData = (
+ queryClient: QueryClient,
+ queryKey: string,
+ playlistId: number,
+ update: (playlist: PlayListDataProps) => PlayListDataProps
+) => {
+ queryClient.setQueriesData(
+ { queryKey: [queryKey] },
+ (oldData) => {
+ if (!oldData) return oldData;
+ return {
+ ...oldData,
+ pages: oldData.pages.map((page) => ({
+ ...page,
+ playlistsData: page.playlistsData.map((playlist) =>
+ playlist.playlistId === playlistId.toString()
+ ? update(playlist)
+ : playlist
+ ),
+ })),
+ };
+ }
+ );
+};
+
+const usePlaylistActions = (playlistId: number, initialLikes: number) => {
+ const queryClient = useQueryClient();
+ const {
+ likedPlaylist,
+ savedPlaylist,
+ user,
+ addLikedPlaylistItem,
+ removeLikedPlaylistItem,
+ addSavedPlaylistItem,
+ removeSavedPlaylistItem,
+ } = useAuthStore();
+ const { updatePlaylists } = useUpdateUserPlaylists();
+ const { toastTrigger } = useToast();
+ const location = useLocation();
+ const page = location.pathname.split('/')[1];
+ const queryKey = PAGES_QUERY[page];
+
+ const isLiked = likedPlaylist.includes(playlistId);
+ const isAdded = savedPlaylist.includes(playlistId);
+ const [initLikes, setInitLikes] = useState(initialLikes);
+
+ const toggleLike = async () => {
+ if (!user) {
+ toastTrigger('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.', 'fail');
+ return;
+ }
+
+ const newIsLiked = !isLiked;
+ const newLikes = newIsLiked ? initLikes + 1 : initLikes - 1;
+ setInitLikes(newLikes);
+
+ updatePlaylistData(queryClient, queryKey, playlistId, (playlist) => ({
+ ...playlist,
+ isLiked: newIsLiked,
+ likes: newLikes,
+ }));
+
+ if (newIsLiked) {
+ addLikedPlaylistItem(playlistId);
+ } else {
+ removeLikedPlaylistItem(playlistId);
+ }
+
+ try {
+ await updatePlaylists({
+ playlistType: 'liked',
+ playlistId,
+ newLikes,
+ });
+ } catch (error) {
+ toastTrigger('์ข์์๋ฅผ ์คํจํ์ต๋๋ค.', 'fail');
+ if (newIsLiked) {
+ removeLikedPlaylistItem(playlistId);
+ } else {
+ addLikedPlaylistItem(playlistId);
+ }
+ }
+ };
+
+ const toggleSave = async () => {
+ if (!user) {
+ toastTrigger('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.', 'fail');
+ return;
+ }
+
+ const newIsAdded = !isAdded;
+
+ updatePlaylistData(queryClient, queryKey, playlistId, (playlist) => ({
+ ...playlist,
+ isAdded: newIsAdded,
+ }));
+
+ if (newIsAdded) {
+ addSavedPlaylistItem(playlistId);
+ } else {
+ removeSavedPlaylistItem(playlistId);
+ }
+
+ try {
+ await updatePlaylists({ playlistType: 'saved', playlistId });
+ } catch (error) {
+ toastTrigger('์ ์ฅ์ ์คํจํ์ต๋๋ค.', 'fail');
+ if (newIsAdded) {
+ removeSavedPlaylistItem(playlistId);
+ } else {
+ addSavedPlaylistItem(playlistId);
+ }
+ }
+ };
+
+ return {
+ isLiked,
+ isAdded,
+ toggleLike,
+ toggleSave,
+ likes: initLikes,
+ setInitLikes,
+ };
+};
+
+export default usePlaylistActions;
diff --git a/src/hooks/usePlaylistInfo.ts b/src/hooks/usePlaylistInfo.ts
new file mode 100644
index 0000000..5297b31
--- /dev/null
+++ b/src/hooks/usePlaylistInfo.ts
@@ -0,0 +1,139 @@
+import { useCallback, useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import { CommentWithProfileApiProps, getPlaylistComment } from '@/api/comment';
+import { getPlaylistInfo } from '@/api/playlistInfo';
+import { getUserInfo } from '@/api/profileInfo';
+import { getVideoInfo } from '@/api/video';
+import { AddedLinkProps } from '@/components/playlist/AddedVideo';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { UserProps } from '@/types/api';
+import { PlayListDataProps } from '@/types/playlistType';
+
+const INIT_VALUES: DetailInfoProps = {
+ playlistInfo: {
+ isPublic: true,
+ likes: 0,
+ links: [],
+ ownerChannelName: '๋ก๋ฉ์ค...',
+ regDate: new Date().toISOString(),
+ tags: [],
+ thumbnail: '',
+ title: '๋ก๋ฉ์ค...',
+ userId: '๋ก๋ฉ์ค...',
+ },
+ comments: [],
+ ownerInfo: {
+ channelFollower: [],
+ channelFollowing: [],
+ channelName: '์ค๋์ฑ๋๋ช
',
+ isMyChannel: false,
+ profileImg: '/src/assets/profile_default.png',
+ userId: '',
+ },
+};
+
+interface DetailInfoProps {
+ playlistInfo: PlayListDataProps;
+ comments: CommentWithProfileApiProps[];
+ ownerInfo: UserProps;
+}
+
+export const usePlaylistInfo = () => {
+ const { userId: loginId } = useAuthStore();
+ const { playlistId } = useParams<{ playlistId: string }>();
+
+ const [detailInfo, setDetailInfo] = useState(INIT_VALUES);
+
+ const [playlistInfo, setPlaylistInfo] = useState(
+ INIT_VALUES.playlistInfo
+ );
+ const [videoList, setVideoList] = useState([]);
+ const [ownerInfo, setOwnerInfo] = useState(INIT_VALUES.ownerInfo);
+ const [commentList, setCommentList] = useState(
+ INIT_VALUES.comments
+ );
+
+ const [isLoading, setIsLoading] = useState(false);
+
+ const fetchPlaylistInfo = useCallback(async () => {
+ const { status, result } = await getPlaylistInfo(Number(playlistId));
+ if (status === 'fail') {
+ throw new Error('๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ์ ์คํจํ์ต๋๋ค.');
+ }
+ if (result) {
+ setPlaylistInfo(result);
+ }
+ }, [playlistId]);
+
+ const fetchOwnerInfo = useCallback(async () => {
+ const { status, result } = await getUserInfo(playlistInfo.userId);
+ if (status === 'fail') {
+ throw new Error('๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ์ ์คํจํ์ต๋๋ค.');
+ }
+ if (result) {
+ setOwnerInfo({
+ ...result,
+ userId: playlistInfo.userId,
+ isMyChannel: playlistInfo.userId === loginId,
+ });
+ }
+ }, [playlistInfo]);
+
+ const convertLinkToVideoInfo = useCallback(
+ async (links: string[]) => {
+ const tempList: AddedLinkProps[] = new Array(links.length);
+ await Promise.all(
+ links.map(async (link, index) => {
+ const { result } = await getVideoInfo(link);
+ if (result) tempList[index] = result;
+ })
+ );
+ setVideoList(tempList);
+ },
+ [playlistInfo]
+ );
+
+ const fetchCommentInfo = useCallback(async () => {
+ const { status, result } = await getPlaylistComment(Number(playlistId));
+ if (status === 'fail') {
+ throw new Error('๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ์ ์คํจํ์ต๋๋ค.');
+ }
+ if (result) {
+ setCommentList(result);
+ }
+ }, [playlistInfo]);
+
+ useEffect(() => {
+ fetchPlaylistInfo();
+ fetchOwnerInfo();
+ fetchCommentInfo();
+ }, []);
+
+ useEffect(() => {
+ convertLinkToVideoInfo(playlistInfo.links);
+ }, [playlistInfo]);
+
+ useEffect(() => {
+ setIsLoading(
+ playlistInfo !== INIT_VALUES.playlistInfo &&
+ ownerInfo !== INIT_VALUES.ownerInfo &&
+ commentList !== INIT_VALUES.comments
+ );
+
+ setDetailInfo({
+ ...detailInfo,
+ playlistInfo,
+ ownerInfo,
+ comments: commentList,
+ });
+ }, [playlistInfo, ownerInfo, commentList]);
+
+ return {
+ detailInfo,
+ videoList,
+ isLoading,
+ fetchPlaylistInfo,
+ fetchOwnerInfo,
+ fetchCommentInfo,
+ };
+};
diff --git a/src/hooks/useSearchPlaylist.ts b/src/hooks/useSearchPlaylist.ts
new file mode 100644
index 0000000..d45e6a1
--- /dev/null
+++ b/src/hooks/useSearchPlaylist.ts
@@ -0,0 +1,22 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+import { DocumentData, QueryDocumentSnapshot } from 'firebase/firestore';
+import { fetchSearchPlaylists } from '@/api/searchPlaylists';
+import { PlayListDataProps } from '@/types/playlistType';
+
+export const useFetchSearchPlaylists = (
+ keyword: string,
+ pageSize: number = 5
+) =>
+ useInfiniteQuery<{
+ playlistsData: PlayListDataProps[];
+ nextCursor: QueryDocumentSnapshot | null;
+ }>({
+ queryKey: ['searchPlaylists', keyword],
+ queryFn: async ({ pageParam }) => {
+ const snapshot = pageParam as QueryDocumentSnapshot | null;
+ return fetchSearchPlaylists(keyword, snapshot, pageSize);
+ },
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ enabled: Boolean(keyword),
+ initialPageParam: null,
+ });
diff --git a/src/hooks/useTagFetch.ts b/src/hooks/useTagFetch.ts
new file mode 100644
index 0000000..a9baa27
--- /dev/null
+++ b/src/hooks/useTagFetch.ts
@@ -0,0 +1,95 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+import {
+ query,
+ where,
+ orderBy,
+ collection,
+ limit,
+ getDocs,
+ startAfter,
+ DocumentData,
+ Query,
+} from 'firebase/firestore';
+import { getCommentCount } from '@/api/myplaylists';
+import { db } from '@/firebase/firbaseConfig';
+import { PlayListDataProps } from '@/types/playlistType';
+
+const fetchPlaylistsByTag = async (
+ tag: string,
+ pageParam: number,
+ pageSize: number
+): Promise<{
+ playlistsData: PlayListDataProps[];
+ nextCursor: number | null;
+}> => {
+ const playlistCollection = collection(db, 'playlist');
+
+ const fetchPlaylists = async (q: Query) => {
+ const querySnapshot = await getDocs(q);
+
+ return await Promise.all(
+ querySnapshot.docs.map(async (doc) => ({
+ playlistId: doc.id,
+ commentCount: await getCommentCount(doc.id),
+ ...(doc.data() as Omit<
+ PlayListDataProps,
+ 'playlistId' | 'commentCount'
+ >),
+ }))
+ );
+ };
+
+ let q: Query;
+
+ if (tag === '#์ธ๊ธฐ ๊ธ์์น ๋์์') {
+ q = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ orderBy('likes', 'desc'),
+ limit(pageSize)
+ );
+ } else {
+ q = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ where('tags', 'array-contains', tag),
+ orderBy('likes', 'desc'),
+ limit(pageSize)
+ );
+ }
+
+ if (pageParam > 0) {
+ const previousQuery = query(
+ playlistCollection,
+ where('isPublic', '==', true),
+ tag !== '#์ธ๊ธฐ ๊ธ์์น ๋์์'
+ ? where('tags', 'array-contains', tag)
+ : where('isPublic', '==', true),
+ orderBy('likes', 'desc'),
+ limit(pageParam * pageSize)
+ );
+ const previousSnapshot = await getDocs(previousQuery);
+ const lastDoc = previousSnapshot.docs[previousSnapshot.docs.length - 1];
+
+ if (lastDoc) {
+ q = query(q, startAfter(lastDoc));
+ }
+ }
+
+ const allPlaylists = await fetchPlaylists(q);
+ const nextCursor = allPlaylists.length < pageSize ? null : pageParam + 1;
+
+ return { playlistsData: allPlaylists, nextCursor };
+};
+
+const useTagFetch = (tag: string, pageSize: number = 5) =>
+ useInfiniteQuery({
+ queryKey: ['popularPlaylists', tag],
+ queryFn: ({ pageParam = 0 }) =>
+ fetchPlaylistsByTag(tag, pageParam, pageSize),
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ initialPageParam: 0,
+ enabled: !!tag,
+ });
+
+export default useTagFetch;
diff --git a/src/hooks/useTagSelection.ts b/src/hooks/useTagSelection.ts
new file mode 100644
index 0000000..ecb7676
--- /dev/null
+++ b/src/hooks/useTagSelection.ts
@@ -0,0 +1,28 @@
+import { useState } from 'react';
+
+const useTagSelection = (initialSelectedTags: string[] = []) => {
+ const [selectedTags, setSelectedTags] =
+ useState(initialSelectedTags);
+
+ const onTagSelection = (label: string, removable: boolean) => {
+ if (removable) return;
+
+ setSelectedTags((prev) => {
+ if (prev.includes(label)) {
+ return prev.filter((tagLabel) => tagLabel !== label);
+ } else {
+ if (prev.length >= 10) {
+ return prev;
+ }
+ return [...prev, label];
+ }
+ });
+ };
+
+ return {
+ selectedTags,
+ onTagSelection,
+ };
+};
+
+export default useTagSelection;
diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.ts
new file mode 100644
index 0000000..fe1ff1c
--- /dev/null
+++ b/src/hooks/useToast.ts
@@ -0,0 +1,25 @@
+import { ToastStatusType } from '@/components/Toast';
+import { useToastStore } from '@/stores/useToastStore';
+
+const useToast = () => {
+ const { isToastOn, toastMsg, toastOn, toastOff, status } = useToastStore();
+
+ const handler = {
+ toastTrigger: (msg: string, status: ToastStatusType = 'success') => {
+ toastOn(msg, status);
+ },
+ onClose: () => {
+ toastOff();
+ },
+ };
+
+ return {
+ isToastOn,
+ toastMsg,
+ status,
+ toastTrigger: handler.toastTrigger,
+ onClose: handler.onClose,
+ };
+};
+
+export default useToast;
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 6119ad9..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,68 +0,0 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
-}
diff --git a/src/main.tsx b/src/main.tsx
index 6f4ac9b..e10563a 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,10 +1,9 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import App from './App.tsx'
-import './index.css'
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import App from '@/App.tsx';
createRoot(document.getElementById('root')!).render(
- ,
-)
+
+);
diff --git a/src/mock/comment-data.json b/src/mock/comment-data.json
new file mode 100644
index 0000000..f6e88a3
--- /dev/null
+++ b/src/mock/comment-data.json
@@ -0,0 +1,23 @@
+[
+ {
+ "playlistId": 1,
+ "userId": "test1234",
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "isEditted": true,
+ "content": "์ด๊ฑฐ ๋๋ฌด ์ข์์"
+ },
+ {
+ "playlistId": 1,
+ "userId": "kimisadev27",
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "isEditted": false,
+ "content": "2024๋
์ต๊ณ ์ ์์"
+ },
+ {
+ "playlistId": 2,
+ "userId": "test1234",
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "isEditted": false,
+ "content": "์ด๊ฑฐ ๋๋ฌด ์ข์์(๋ด์๋)"
+ }
+]
diff --git a/src/mock/following-test.ts b/src/mock/following-test.ts
new file mode 100644
index 0000000..e50716f
--- /dev/null
+++ b/src/mock/following-test.ts
@@ -0,0 +1,115 @@
+import { PlayListDataProps } from '@/hooks/usePlaylist';
+
+interface ProfileProps {
+ src: string;
+ alt: string;
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+}
+
+export interface TestProfileProps extends ProfileProps {
+ name: string;
+}
+
+export interface TestDataProps {
+ following: TestProfileProps[];
+ playlist: PlayListDataProps[];
+}
+
+export const TEST_DATA: TestDataProps = {
+ following: [
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์1',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail2.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์2',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์3',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail2.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์4',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail2.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์4',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail2.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์4',
+ },
+ {
+ alt: '์ธ๋ค์ผ',
+ src: '/src/assets/defaultThumbnail2.jpg',
+ size: 'md',
+ name: '์ฌ์ฉ์4',
+ },
+ ],
+ playlist: [
+ {
+ title: 'ํ ์ฌ๋ฆ ์ฝ๋ฉํ๋ฉฐ ๋ฃ๋ ๋กํ์ด ๐',
+ userName: 'user1',
+ tags: ['#๋ฐ๋ผ๋', '#ํํฉ'],
+ numberOfComments: 20,
+ numberOfLikes: 100,
+ publicity: false,
+ links: [
+ '/src/assets/defaultThumbnail.jpg',
+ '/src/assets/defaultThumbnail2.jpg',
+ '/src/assets/defaultThumbnail.jpg',
+ ],
+ },
+ {
+ title: 'ํ๋ผํ ์ฌํ๊ฐ๊ณ ์ถ์ด์ง๋ ์์๐',
+ userName: 'user2',
+ tags: ['#์ฌํ', '#ํ๋ผํ', '#๊ทค'],
+ publicity: true,
+ numberOfComments: 2000,
+ numberOfLikes: 1000,
+ links: [
+ '/src/assets/defaultThumbnail2.jpg',
+ '/src/assets/defaultThumbnail.jpg',
+ ],
+ },
+ {
+ title: 'ํ ์ฌ๋ฆ ์ฝ๋ฉํ๋ฉฐ ๋ฃ๋ ๋กํ์ด ๐',
+ userName: 'user1',
+ tags: ['#๋ฐ๋ผ๋', '#ํํฉ'],
+ numberOfComments: 20,
+ numberOfLikes: 100,
+ publicity: false,
+ links: [
+ '/src/assets/defaultThumbnail.jpg',
+ '/src/assets/defaultThumbnail2.jpg',
+ '/src/assets/defaultThumbnail.jpg',
+ ],
+ },
+ {
+ title: 'ํ๋ผํ ์ฌํ๊ฐ๊ณ ์ถ์ด์ง๋ ์์๐',
+ userName: 'user2',
+ tags: ['#์ฌํ', '#ํ๋ผํ', '#๊ทค'],
+ publicity: true,
+ numberOfComments: 2000,
+ numberOfLikes: 1000,
+ links: [
+ '/src/assets/defaultThumbnail2.jpg',
+ '/src/assets/defaultThumbnail.jpg',
+ ],
+ },
+ ],
+};
diff --git a/src/mock/playlist-data.json b/src/mock/playlist-data.json
new file mode 100644
index 0000000..8237e8c
--- /dev/null
+++ b/src/mock/playlist-data.json
@@ -0,0 +1,448 @@
+[
+ {
+ "docid": "1",
+ "userId": "test1234",
+ "tags": ["#์์
", "#์ฝ๋ฉ", "#๊ฒฝ๊ฑดํด์ง๋ ๋ง์"],
+ "links": [
+ "https://www.youtube.com/watch?v=n6B5gQXlB-0",
+ "https://www.youtube.com/watch?v=lzm-7YfBJBU",
+ "https://www.youtube.com/watch?v=yss4rIrHl6o"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_022b0e10-ed50-47b9-9b4b-d686d8fa170a.jpg?alt=media&token=9d1ca5cf-9699-44aa-9051-b4d895a61f87",
+ "likes": 5000,
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "description": "๊ฒฝ๊ฑดํด์ง๋ ์ธ๊ธ๋ ๋
ธ๋๋ชจ์",
+ "title": "์ธ๊ธ๋ ๋
ธ๋๋ชจ์ 9์3์ผ",
+ "isPublic": true
+ },
+ {
+ "docid": "2",
+ "userId": "test1234",
+ "tags": ["#์์
", "#์ฝ๋ฉ"],
+ "links": [
+ "https://www.youtube.com/watch?v=n6B5gQXlB-0",
+ "https://www.youtube.com/watch?v=yss4rIrHl6o"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_15b30316-4861-4c9c-a470-9ca00d022bbf.jpg?alt=media&token=4fed1331-d47f-41c6-a5ea-7a3d1a7375d3",
+ "likes": 5000,
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "description": "๊ฒฝ๊ฑดํด์ง๋ ์ธ๊ธ๋ ๋
ธ๋๋ชจ์2",
+ "title": "์ธ๊ธ๋ ๋
ธ๋๋ชจ์ 9์3์ผ 2ํ์ฐจ",
+ "isPublic": true
+ },
+ {
+ "docid": "3",
+ "userId": "test1234",
+ "tags": ["#ํ", "#๋๋ผ๋ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_846fb20a-aca5-4788-bb95-4aff67abd690.jpg?alt=media&token=9c75e077-738e-4722-af3f-2797ec55e37a",
+ "likes": 34,
+ "regDate": "2024-09-02T17:55:50.181Z",
+ "description": "ํ๊ณผ ๋๋ผ๋ง ์ฅ๋ฅด์ ๋ค์ํ ๋งค๋ ฅ์ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋ค์ํ ํ๊ณผ ๋๋ผ๋ง",
+ "isPublic": true
+ },
+ {
+ "docid": "4",
+ "userId": "test1234",
+ "tags": ["#๋ก", "#ํํ์ง"],
+ "links": ["https://www.youtube.com/watch?v=05BmN1WX_xw"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a7dea79d-d0a8-4841-bc76-10674fc581c2.jpg?alt=media&token=c3d09ac3-9764-4770-baf4-34fb87062537",
+ "likes": 22,
+ "regDate": "2024-08-30-14-00",
+ "description": "๋ก๊ณผ ํํ์ง ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ๋ก๊ณผ ํํ์ง",
+ "isPublic": false
+ },
+ {
+ "docid": "5",
+ "userId": "test1234",
+ "tags": ["#์ฌ์ฆ", "#์ผ๋ ํธ๋ก๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ",
+ "https://www.youtube.com/watch?v=_41K4CpsZwg"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_7f700cef-e503-4304-b38b-73d3a5302653.jpg?alt=media&token=395a16db-ac49-462c-a76c-0236b051e519",
+ "likes": 19,
+ "regDate": "2024-08-30-16-20",
+ "description": "์ฌ์ฆ์ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ๋
ํนํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋
ํนํ ์ฌ์ฆ์ ์ผ๋ ํธ๋ก๋",
+ "isPublic": true
+ },
+ {
+ "docid": "6",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#๋๋ผ๋ง"],
+ "links": ["https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_b938fb8c-c88c-4894-b4f7-f33661bb7c63.jpg?alt=media&token=7907b7cc-7dc7-499c-861a-789aa04deda6",
+ "likes": 26,
+ "regDate": "2024-08-30-13-50",
+ "description": "ํธ๋ฌ์ ๋๋ผ๋ง ์ฅ๋ฅด์ ๊ฐ์ฑ์ ์ธ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๊ฐ์ฑ์ ์ธ ํธ๋ฌ์ ๋๋ผ๋ง",
+ "isPublic": false
+ },
+ {
+ "docid": "7",
+ "userId": "test1234",
+ "tags": ["#์ฝ๋ฏธ๋", "#์ธ๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=05BmN1WX_xw"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a2aa0e69-7920-4054-a9b4-38efef560734.jpg?alt=media&token=bcc7f0a6-5eb2-425b-b8e6-b978edad2274",
+ "likes": 24,
+ "regDate": "2024-08-30-18-10",
+ "description": "์ฝ๋ฏธ๋์ ์ธ๋ ์ฅ๋ฅด์ ์ ์พํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ์พํ ์ฝ๋ฏธ๋์ ์ธ๋",
+ "isPublic": true
+ },
+ {
+ "docid": "8",
+ "userId": "test1234",
+ "tags": ["#์ฌ์ฆ", "#๋๋ผ๋ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_ad78363d-aa13-4c6c-8da3-5222b716ea4b.jpg?alt=media&token=f8b14f97-a6e3-41dd-b82a-96ff449e9960",
+ "likes": 28,
+ "regDate": "2024-08-30-15-00",
+ "description": "์ฌ์ฆ์ ๋๋ผ๋ง ์ฅ๋ฅด์ ๊ฐ์ฑ์ ์ธ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๊ฐ์ฑ์ ์ธ ์ฌ์ฆ์ ๋๋ผ๋ง",
+ "isPublic": false
+ },
+ {
+ "docid": "9",
+ "userId": "test1234",
+ "tags": ["#ํ", "#์ผ๋ ํธ๋ก๋"],
+ "links": ["https://www.youtube.com/watch?v=05BmN1WX_xw"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_846fb20a-aca5-4788-bb95-4aff67abd690.jpg?alt=media&token=9c75e077-738e-4722-af3f-2797ec55e37a",
+ "likes": 20,
+ "regDate": "2024-08-30-17-30",
+ "description": "ํ๊ณผ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ํ๊ณผ ์ผ๋ ํธ๋ก๋",
+ "isPublic": true
+ },
+ {
+ "docid": "10",
+ "userId": "test1234",
+ "tags": ["#ํํ์ง", "#๋๋ผ๋ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_15b30316-4861-4c9c-a470-9ca00d022bbf.jpg?alt=media&token=4fed1331-d47f-41c6-a5ea-7a3d1a7375d3",
+ "likes": 29,
+ "regDate": "2024-08-30-11-45",
+ "description": "ํํ์ง์ ๋๋ผ๋ง ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ํํ์ง์ ๋๋ผ๋ง",
+ "isPublic": true
+ },
+ {
+ "docid": "11",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#์ธ๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a7dea79d-d0a8-4841-bc76-10674fc581c2.jpg?alt=media&token=c3d09ac3-9764-4770-baf4-34fb87062537",
+ "likes": 27,
+ "regDate": "2024-08-30-19-10",
+ "description": "ํธ๋ฌ์ ์ธ๋ ์ฅ๋ฅด์ ๋
ํนํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋
ํนํ ํธ๋ฌ์ ์ธ๋",
+ "isPublic": false
+ },
+
+ {
+ "docid": "12",
+ "userId": "test1234",
+ "tags": ["#์ฌ์ฆ", "#์ฝ๋ฏธ๋"],
+ "links": ["https://www.youtube.com/watch?v=_41K4CpsZwg"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a2aa0e69-7920-4054-a9b4-38efef560734.jpg?alt=media&token=bcc7f0a6-5eb2-425b-b8e6-b978edad2274",
+ "likes": 21,
+ "regDate": "2024-08-30-12-15",
+ "description": "์ฌ์ฆ์ ์ฝ๋ฏธ๋ ์ฅ๋ฅด์ ์ฆ๊ฑฐ์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ฆ๊ฑฐ์ด ์ฌ์ฆ์ ์ฝ๋ฏธ๋",
+ "isPublic": true
+ },
+ {
+ "docid": "13",
+ "userId": "test1234",
+ "tags": ["#์ฝ๋ฏธ๋", "#ํ"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=05BmN1WX_xw"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_ad78363d-aa13-4c6c-8da3-5222b716ea4b.jpg?alt=media&token=f8b14f97-a6e3-41dd-b82a-96ff449e9960",
+ "likes": 26,
+ "regDate": "2024-08-30-20-00",
+ "description": "์ฝ๋ฏธ๋์ ํ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ์ฝ๋ฏธ๋์ ํ",
+ "isPublic": false
+ },
+ {
+ "docid": "14",
+ "userId": "test1234",
+ "tags": ["#์ผ๋ ํธ๋ก๋", "#ํํ์ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=05BmN1WX_xw"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_846fb20a-aca5-4788-bb95-4aff67abd690.jpg?alt=media&token=9c75e077-738e-4722-af3f-2797ec55e37a",
+ "likes": 30,
+ "regDate": "2024-08-30-21-20",
+ "description": "์ผ๋ ํธ๋ก๋๊ณผ ํํ์ง ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ์ผ๋ ํธ๋ก๋๊ณผ ํํ์ง",
+ "isPublic": true
+ },
+ {
+ "docid": "15",
+ "userId": "test1234",
+ "tags": ["#๋๋ผ๋ง", "#ํ"],
+ "links": [
+ "https://www.youtube.com/watch?v=05BmN1WX_xw",
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_7f700cef-e503-4304-b38b-73d3a5302653.jpg?alt=media&token=395a16db-ac49-462c-a76c-0236b051e519",
+ "likes": 25,
+ "regDate": "2024-08-30-10-30",
+ "description": "๋๋ผ๋ง์ ํ ์ฅ๋ฅด์ ๊ฐ์ฑ์ ์ธ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๊ฐ์ฑ์ ์ธ ๋๋ผ๋ง์ ํ",
+ "isPublic": true
+ },
+ {
+ "docid": "16",
+ "userId": "test1234",
+ "tags": ["#์ฝ๋ฏธ๋", "#์ผ๋ ํธ๋ก๋"],
+ "links": ["https://www.youtube.com/watch?v=6CHs4x2uqcQ"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a7dea79d-d0a8-4841-bc76-10674fc581c2.jpg?alt=media&token=c3d09ac3-9764-4770-baf4-34fb87062537",
+ "likes": 23,
+ "regDate": "2024-08-30-11-30",
+ "description": "์ฝ๋ฏธ๋์ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ์ฝ๋ฏธ๋์ ์ผ๋ ํธ๋ก๋",
+ "isPublic": false
+ },
+ {
+ "docid": "17",
+ "userId": "test1234",
+ "tags": ["#์ธ๋", "#ํํ์ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_022b0e10-ed50-47b9-9b4b-d686d8fa170a.jpg?alt=media&token=9d1ca5cf-9699-44aa-9051-b4d895a61f87",
+ "likes": 27,
+ "regDate": "2024-08-30-13-05",
+ "description": "์ธ๋์ ํํ์ง ์ฅ๋ฅด์ ๋
ํนํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋
ํนํ ์ธ๋์ ํํ์ง",
+ "isPublic": true
+ },
+ {
+ "docid": "18",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#์ผ๋ ํธ๋ก๋"],
+ "links": ["https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_15b30316-4861-4c9c-a470-9ca00d022bbf.jpg?alt=media&token=4fed1331-d47f-41c6-a5ea-7a3d1a7375d3",
+ "likes": 31,
+ "regDate": "2024-08-30-12-00",
+ "description": "ํธ๋ฌ์ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ํธ๋ฌ์ ์ผ๋ ํธ๋ก๋",
+ "isPublic": false
+ },
+ {
+ "docid": "19",
+ "userId": "test1234",
+ "tags": ["#์ฝ๋ฏธ๋", "#์ฌ์ฆ"],
+ "links": [
+ "https://www.youtube.com/watch?v=_41K4CpsZwg",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a2aa0e69-7920-4054-a9b4-38efef560734.jpg?alt=media&token=bcc7f0a6-5eb2-425b-b8e6-b978edad2274",
+ "likes": 26,
+ "regDate": "2024-08-30-14-45",
+ "description": "์ฝ๋ฏธ๋์ ์ฌ์ฆ ์ฅ๋ฅด์ ์ ์พํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ์พํ ์ฝ๋ฏธ๋์ ์ฌ์ฆ",
+ "isPublic": true
+ },
+ {
+ "docid": "20",
+ "userId": "test1234",
+ "tags": ["#๋๋ผ๋ง", "#์ผ๋ ํธ๋ก๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=05BmN1WX_xw",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a7dea79d-d0a8-4841-bc76-10674fc581c2.jpg?alt=media&token=c3d09ac3-9764-4770-baf4-34fb87062537",
+ "likes": 30,
+ "regDate": "2024-08-30-15-30",
+ "description": "๋๋ผ๋ง์ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ๋๋ผ๋ง์ ์ผ๋ ํธ๋ก๋",
+ "isPublic": false
+ },
+ {
+ "docid": "21",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#ํ"],
+ "links": [
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_ad78363d-aa13-4c6c-8da3-5222b716ea4b.jpg?alt=media&token=f8b14f97-a6e3-41dd-b82a-96ff449e9960",
+ "likes": 28,
+ "regDate": "2024-08-30-16-30",
+ "description": "ํธ๋ฌ์ ํ ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ํธ๋ฌ์ ํ",
+ "isPublic": true
+ },
+ {
+ "docid": "22",
+ "userId": "test1234",
+ "tags": ["#์ฌ์ฆ", "#ํํ์ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=05BmN1WX_xw",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_b938fb8c-c88c-4894-b4f7-f33661bb7c63.jpg?alt=media&token=7907b7cc-7dc7-499c-861a-789aa04deda6",
+ "likes": 29,
+ "regDate": "2024-08-30-17-20",
+ "description": "์ฌ์ฆ์ ํํ์ง ์ฅ๋ฅด์ ๊ฐ์ฑ์ ์ธ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๊ฐ์ฑ์ ์ธ ์ฌ์ฆ์ ํํ์ง",
+ "isPublic": false
+ },
+ {
+ "docid": "23",
+ "userId": "test1234",
+ "tags": ["#์ฝ๋ฏธ๋", "#์ฌ์ฆ"],
+ "links": ["https://www.youtube.com/watch?v=05BmN1WX_xw"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_ad78363d-aa13-4c6c-8da3-5222b716ea4b.jpg?alt=media&token=f8b14f97-a6e3-41dd-b82a-96ff449e9960",
+ "likes": 25,
+ "regDate": "2024-08-30-18-00",
+ "description": "์ฝ๋ฏธ๋์ ์ฌ์ฆ ์ฅ๋ฅด์ ๋
ํนํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋
ํนํ ์ฝ๋ฏธ๋์ ์ฌ์ฆ",
+ "isPublic": true
+ },
+ {
+ "docid": "24",
+ "userId": "test1234",
+ "tags": ["#์ธ๋", "#๋๋ผ๋ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_846fb20a-aca5-4788-bb95-4aff67abd690.jpg?alt=media&token=9c75e077-738e-4722-af3f-2797ec55e37a",
+ "likes": 24,
+ "regDate": "2024-08-30-19-45",
+ "description": "์ธ๋์ ๋๋ผ๋ง ์ฅ๋ฅด์ ๋ค์ํ ๋งค๋ ฅ์ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋ค์ํ ์ธ๋์ ๋๋ผ๋ง",
+ "isPublic": true
+ },
+ {
+ "docid": "25",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#์ผ๋ ํธ๋ก๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_022b0e10-ed50-47b9-9b4b-d686d8fa170a.jpg?alt=media&token=9d1ca5cf-9699-44aa-9051-b4d895a61f87",
+ "likes": 22,
+ "regDate": "2024-08-30-20-15",
+ "description": "ํธ๋ฌ์ ์ผ๋ ํธ๋ก๋ ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ํธ๋ฌ์ ์ผ๋ ํธ๋ก๋",
+ "isPublic": false
+ },
+ {
+ "docid": "26",
+ "userId": "test1234",
+ "tags": ["#ํ", "#์ฝ๋ฏธ๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=05BmN1WX_xw",
+ "https://www.youtube.com/watch?v=_41K4CpsZwg"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_15b30316-4861-4c9c-a470-9ca00d022bbf.jpg?alt=media&token=4fed1331-d47f-41c6-a5ea-7a3d1a7375d3",
+ "likes": 30,
+ "regDate": "2024-08-30-21-00",
+ "description": "ํ๊ณผ ์ฝ๋ฏธ๋ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ํ๊ณผ ์ฝ๋ฏธ๋",
+ "isPublic": true
+ },
+ {
+ "docid": "27",
+ "userId": "test1234",
+ "tags": ["#์ผ๋ ํธ๋ก๋", "#ํํ์ง"],
+ "links": [
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ",
+ "https://www.youtube.com/watch?v=rNZrgK-MX_s&t=1912s"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_7f700cef-e503-4304-b38b-73d3a5302653.jpg?alt=media&token=395a16db-ac49-462c-a76c-0236b051e519",
+ "likes": 25,
+ "regDate": "2024-08-30-22-10",
+ "description": "์ผ๋ ํธ๋ก๋๊ณผ ํํ์ง ์ฅ๋ฅด์ ๋
ํนํ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋
ํนํ ์ผ๋ ํธ๋ก๋๊ณผ ํํ์ง",
+ "isPublic": false
+ },
+ {
+ "docid": "28",
+ "userId": "test1234",
+ "tags": ["#ํธ๋ฌ", "#์ฌ์ฆ"],
+ "links": ["https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s"],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_846fb20a-aca5-4788-bb95-4aff67abd690.jpg?alt=media&token=9c75e077-738e-4722-af3f-2797ec55e37a",
+ "likes": 28,
+ "regDate": "2024-08-30-23-00",
+ "description": "ํธ๋ฌ์ ์ฌ์ฆ ์ฅ๋ฅด์ ์ ๋น๋ก์ด ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋น๋ก์ด ํธ๋ฌ์ ์ฌ์ฆ",
+ "isPublic": true
+ },
+ {
+ "docid": "29",
+ "userId": "test1234",
+ "tags": ["#๋๋ผ๋ง", "#์ธ๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1",
+ "https://www.youtube.com/watch?v=_41K4CpsZwg"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_a2aa0e69-7920-4054-a9b4-38efef560734.jpg?alt=media&token=bcc7f0a6-5eb2-425b-b8e6-b978edad2274",
+ "likes": 32,
+ "regDate": "2024-08-30-10-45",
+ "description": "๋๋ผ๋ง์ ์ธ๋ ์ฅ๋ฅด์ ๊ฐ์ฑ์ ์ธ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๊ฐ์ฑ์ ์ธ ๋๋ผ๋ง์ ์ธ๋",
+ "isPublic": false
+ },
+ {
+ "docid": "30",
+ "userId": "test1234",
+ "tags": ["#ํ", "#ํธ๋ฌ"],
+ "links": [
+ "https://www.youtube.com/watch?v=PsO6ZnUZI0g&list=RDEMfxjOmi63mKrTIaFmvquTIA&start_radio=1"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_7f700cef-e503-4304-b38b-73d3a5302653.jpg?alt=media&token=395a16db-ac49-462c-a76c-0236b051e519",
+ "likes": 23,
+ "regDate": "2024-08-30-11-15",
+ "description": "ํ๊ณผ ํธ๋ฌ ์ฅ๋ฅด์ ๋ค์ํ ๋งค๋ ฅ์ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "๋ค์ํ ํ๊ณผ ํธ๋ฌ",
+ "isPublic": true
+ },
+ {
+ "docid": "31",
+ "userId": "test1234",
+ "tags": ["#์ผ๋ ํธ๋ก๋", "#์ฝ๋ฏธ๋"],
+ "links": [
+ "https://www.youtube.com/watch?v=KfbFaQJK7Sc&t=121s",
+ "https://www.youtube.com/watch?v=05BmN1WX_xw",
+ "https://www.youtube.com/watch?v=6CHs4x2uqcQ"
+ ],
+ "thumbnail": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/example%2F_b938fb8c-c88c-4894-b4f7-f33661bb7c63.jpg?alt=media&token=7907b7cc-7dc7-499c-861a-789aa04deda6",
+ "likes": 26,
+ "regDate": "2024-08-30-12-30",
+ "description": "์ผ๋ ํธ๋ก๋๊ณผ ์ฝ๋ฏธ๋ ์ฅ๋ฅด์ ์ ๋๋ ์กฐํ๋ฅผ ๋ด์ ํ๋ ์ด๋ฆฌ์คํธ์
๋๋ค.",
+ "title": "์ ๋๋ ์ผ๋ ํธ๋ก๋๊ณผ ์ฝ๋ฏธ๋",
+ "isPublic": false
+ }
+]
diff --git a/src/mock/user-data.json b/src/mock/user-data.json
new file mode 100644
index 0000000..8e3b04e
--- /dev/null
+++ b/src/mock/user-data.json
@@ -0,0 +1,24 @@
+[
+ {
+ "docId": "test1234",
+ "uid": "XyatDtCFQwRMKd8xUQjamzbgxtV2",
+ "tags": ["#์ฌ์ฆ", "#๋
ธ๋"],
+ "channelName": "์ฝ๋ฉ์ํ๋ฆ",
+ "channelFollower": ["Q0gUDbXIfneAZnSxUF16UcXwA6D3"],
+ "channelFollowing": ["Q0gUDbXIfneAZnSxUF16UcXwA6D3"],
+ "profileImg": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/profile%2Ftest1234%2FHeartsping.jpeg?alt=media&token=aada3ec6-08ff-4613-8a04-ea1f2c7690fe",
+ "savedPlaylist": [1, 2, 3],
+ "likedPlaylist": [10, 20, 30]
+ },
+ {
+ "docId": "kimisadev27",
+ "uid": "Q0gUDbXIfneAZnSxUF16UcXwA6D3",
+ "tags": ["#ํ๊ทธ1", "#ํ๊ทธ2"],
+ "channelName": "์ฑํTV",
+ "channelFollower": ["XyatDtCFQwRMKd8xUQjamzbgxtV2"],
+ "channelFollowing": ["XyatDtCFQwRMKd8xUQjamzbgxtV2"],
+ "profileImg": "https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/playlist%2F31%2Fthumbnail.png?alt=media&token=bc8f6385-8c80-4e62-ab08-bd09b6b54952",
+ "savedPlaylist": [1, 2, 3],
+ "likedPlaylist": [1, 2, 3]
+ }
+]
diff --git a/src/pages/common/Detail.tsx b/src/pages/common/Detail.tsx
new file mode 100644
index 0000000..acdb08d
--- /dev/null
+++ b/src/pages/common/Detail.tsx
@@ -0,0 +1,578 @@
+import { useEffect, useState } from 'react';
+import { css } from '@emotion/react';
+import { ExternalLink, Heart, Bookmark, X } from 'lucide-react';
+import { useParams } from 'react-router-dom';
+import { CommentWithProfileApiProps, removeComment } from '@/api/comment';
+import Button from '@/components/Button';
+import Comment from '@/components/Comment';
+import IconButton from '@/components/IconButton';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import MiniPlaylist from '@/components/MiniPlaylist';
+import Modal from '@/components/Modal';
+import AddedVideo, { AddedLinkProps } from '@/components/playlist/AddedVideo';
+import Profile from '@/components/Profile';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import useDetailForm from '@/hooks/useDetailForm';
+import { useFollowToggle } from '@/hooks/useFollowToggle';
+import usePlaylistActions from '@/hooks/usePlaylistActions';
+import { usePlaylistInfo } from '@/hooks/usePlaylistInfo';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { convertUnitNumber, omittedText } from '@/utils/textUtils';
+
+const MAX_LENGTH = {
+ videoTitle: 20,
+ playlistTitle: 20,
+ description: 100,
+ channelName: 10,
+};
+
+const COMMENT_PLUS_SIZE = 5;
+
+const Detail = () => {
+ const { playlistId } = useParams<{ playlistId: string }>();
+ const { profileImage, userId: loginId } = useAuthStore();
+ const { toastTrigger } = useToast();
+ const [currentVideo, setCurrentVideo] = useState();
+
+ const { values, onChanges, onClicks } = useDetailForm();
+
+ const { detailInfo, videoList, isLoading, fetchOwnerInfo, fetchCommentInfo } =
+ usePlaylistInfo();
+
+ const { playlistInfo, comments, ownerInfo } = detailInfo;
+ const [isCommentReload, setIsCommentReload] = useState(false);
+ const [commentList, setCommentList] = useState(
+ []
+ );
+
+ const [isOpen, setIsOpen] = useState(false);
+ const [isFullDesc, setIsFullDesc] = useState(false);
+
+ const { isFollowing, handleFollowToggle } = useFollowToggle(
+ ownerInfo?.userId
+ );
+
+ const [currentLikes, setCurrentLikes] = useState<{
+ isLiked: boolean;
+ likes: number;
+ }>({ isLiked: false, likes: 0 });
+
+ const { isLiked, isAdded, toggleLike, toggleSave, likes, setInitLikes } =
+ usePlaylistActions(playlistId ? +playlistId : 0, playlistInfo.likes);
+
+ const [commentsPlus, setCommentsPlus] = useState(COMMENT_PLUS_SIZE);
+ const onCommentsPlus = () => {
+ setCommentsPlus((prev) => prev + COMMENT_PLUS_SIZE);
+ };
+
+ const deleteComment = async (docId: string | undefined) => {
+ if (docId) {
+ const { status } = await removeComment(docId);
+ if (status === 'success') {
+ toastTrigger('๋๊ธ์ด ์ญ์ ๋์์ต๋๋ค.', 'success');
+ setCommentList((prev) =>
+ prev.filter((comment) => comment.docId !== docId)
+ );
+ }
+ }
+ };
+
+ useEffect(() => {
+ fetchOwnerInfo();
+ setCurrentLikes({ ...currentLikes, likes: playlistInfo.likes });
+ }, [playlistInfo]);
+
+ useEffect(() => {
+ if (isCommentReload) {
+ setIsCommentReload(false);
+ fetchCommentInfo();
+ }
+ }, [isCommentReload]);
+
+ useEffect(() => {
+ setCommentList(comments);
+ }, [comments]);
+
+ useEffect(() => {
+ setCurrentVideo(videoList[values.currentVideoIndex]);
+ }, [values.currentVideoIndex]);
+
+ useEffect(() => {
+ setCurrentVideo(videoList[0]);
+ }, [videoList]);
+
+ useEffect(() => {
+ setCurrentLikes({
+ isLiked,
+ likes: isLiked ? currentLikes.likes + 1 : currentLikes.likes - 1,
+ });
+ }, [isLiked]);
+
+ useEffect(() => {
+ setInitLikes(currentLikes.likes);
+ }, [currentLikes]);
+
+ return !!!isLoading ? (
+
+
+ ๋ก๋ฉ์ค...
+
+ ) : (
+
+ {currentVideo && (
+
+
+

+ ์์์ ๋ถ๋ฌ์ค๋ ์ค...
+
`,
+ }}
+ >
+
+ )}
+
+ {currentVideo && (
+
+
{currentVideo?.title}
+
+ {playlistInfo.tags.map((tag, index) => (
+
{tag}
+ ))}
+
+
+ )}
+
+ {!!!isOpen && (
+
+
+
+
+ {playlistInfo.title}
+
+ {values.currentVideoIndex + 1}/{playlistInfo.links.length}
+
+
+
{
+ setIsOpen(true);
+ }}
+ color='gray'
+ />
+
+
+ {isFullDesc ? (
+ <>
+ {playlistInfo.description}
+ {
+ setIsFullDesc(false);
+ }}
+ >
+ ์ค์ด๊ธฐ
+
+ >
+ ) : (
+ <>
+
+ {omittedText(
+ playlistInfo.description ?? '',
+ MAX_LENGTH.description
+ )}
+
+ {playlistInfo.description &&
+ playlistInfo.description.length >
+ MAX_LENGTH.description && (
+ {
+ setIsFullDesc(true);
+ }}
+ >
+ ๋๋ณด๊ธฐ
+
+ )}
+ >
+ )}
+
+
+
{
+ onClicks.profile(ownerInfo.userId);
+ }}
+ />
+ {
+ onClicks.profile(ownerInfo.userId);
+ }}
+ >
+
+ {omittedText(ownerInfo.channelName, MAX_LENGTH.channelName)}
+
+
+ {convertUnitNumber(ownerInfo.channelFollower.length, 1)}
+ ํ๋ก์
+
+
+
+ {!!!ownerInfo.isMyChannel && (
+ {
+ if (!!!loginId) toastTrigger('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค', 'fail');
+ handleFollowToggle();
+ fetchOwnerInfo();
+ }}
+ />
+ )}
+
+
+
+
+ {
+ toggleLike();
+ }}
+ color={isLiked ? 'red' : 'gray'}
+ fillColor={isLiked ? 'red' : undefined}
+ label={`${currentLikes.likes}`}
+ />
+
+
+
+
+
+
+ {videoList &&
+ videoList.map((video, index) => (
+
{
+ onClicks.video(index);
+ }}
+ isActive={index === values.currentVideoIndex}
+ provider={video.provider}
+ />
+ ))}
+
+
+ )}
+
+
+
+ ๋๊ธ
+ {commentList.length}
+
+
+
+
) => {
+ if (e.key === 'Enter') {
+ onClicks.comment();
+ setIsCommentReload(true);
+ }
+ }}
+ />
+
{
+ onClicks.comment();
+ setIsCommentReload(true);
+ }}
+ color='black'
+ />
+
+
+ {commentList &&
+ commentList.length > 0 &&
+ commentList.slice(0, commentsPlus).map((comment, index) => (
+ {
+ onClicks.profile(comment.userId);
+ }}
+ />
+ ))}
+
+ {commentList.length > commentsPlus && (
+
+ )}
+
+
+
+ {playlistInfo && isOpen && (
+ {
+ setIsOpen(false);
+ }}
+ videoInfo={{
+ playlistName: omittedText(
+ playlistInfo.title,
+ MAX_LENGTH.playlistTitle
+ ),
+ subject: omittedText(
+ currentVideo?.title ?? '',
+ MAX_LENGTH.videoTitle
+ ),
+ thumbnail: currentVideo?.imgUrl,
+ }}
+ />
+ )}
+
+
+ );
+};
+
+const loadingStyle = css`
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ text-align: -webkit-center;
+`;
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 60px;
+`;
+
+const infoContainerStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding: 12px;
+`;
+
+const currentVideoStyle = css`
+ width: 430px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+ height: 242px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+ display: block;
+
+ iframe {
+ width: 430px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+ height: 242px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+ position: fixed; // ์์ ์๋จ ๊ณ ์
+ z-index: 1000;
+ background-color: black;
+ }
+
+ .emptyVideo {
+ width: 200px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+ height: 200px; // ์์ ์๋จ ๊ณ ์ ์ ํ์ํ ๊ฐ
+
+ display: flex;
+ flex-direction: column;
+ text-align: center;
+ }
+`;
+
+const videoTextDivStyle = css`
+ padding: 12px 12px 0 12px;
+ z-index: 0;
+ & > * {
+ z-index: 0;
+ }
+`;
+
+const videoTitleStyle = css`
+ font-size: ${fontSize.xl};
+ font-weight: ${fontWeight.extraBold};
+ margin: 5px 0;
+
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+`;
+
+const playlistTagStyle = css`
+ padding: 5px 0;
+ display: flex;
+ gap: 5px;
+ color: ${colors.primaryNormal};
+ font-size: ${fontSize.sm};
+`;
+
+const playlistInfoStyle = css`
+ display: flex;
+ flex-direction: column;
+ max-width: 430px;
+ border: 1px solid ${colors.gray02};
+ border-radius: 10px;
+ gap: 5px;
+ margin: 12px;
+`;
+
+const oneLineStyle = css`
+ display: flex;
+ flex-direction: row;
+`;
+
+const headerDetailStyle = css`
+ flex-grow: 1;
+ align-content: center;
+ > .title {
+ font-size: ${fontSize.lg};
+ font-weight: ${fontWeight.bold};
+ }
+
+ > .counter {
+ padding-left: 10px;
+ color: ${colors.gray04};
+ }
+`;
+
+const descStyle = css`
+ color: ${colors.gray05};
+ font-size: ${fontSize.sm};
+ > .extraText {
+ padding-left: 5px;
+ color: ${colors.gray03};
+ cursor: pointer;
+ &:hover {
+ color: ${colors.primaryLight};
+ }
+ }
+`;
+
+const userInfoTwoLineStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding-left: 10px;
+ cursor: pointer;
+
+ > .channelName {
+ font-size: ${fontSize.md};
+ }
+ > .counter {
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ }
+`;
+const emptyBoxStyle = css`
+ flex-grow: 1;
+`;
+
+const userInfoStyle = css`
+ ${oneLineStyle};
+
+ display: flex;
+ justify-content: space-evenly;
+ border-top: 1px solid ${colors.gray02};
+ border-bottom: 1px solid ${colors.gray02};
+ padding: 6px 0 6px 0;
+`;
+
+const videoListInfoStyle = (isScroll: boolean) => css`
+ ${oneLineStyle};
+ flex-direction: column;
+
+ ${isScroll &&
+ css`
+ max-height: 200px;
+ overflow-y: auto;
+ overflow-x: hidden;
+
+ ::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-thumb {
+ background-color: ${colors.gray06};
+ border-radius: 6px;
+ }
+ &::-webkit-scrollbar {
+ width: 8px;
+ }
+ `}
+`;
+
+const commentContainer = css`
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ padding: 20px;
+ gap: 16px;
+`;
+
+const commentHeader = css`
+ ${oneLineStyle};
+
+ > .title {
+ font-weight: ${fontWeight.bold};
+ }
+
+ > .counter {
+ color: ${colors.gray05};
+ padding-left: 5px;
+ }
+`;
+
+const commentInputStyle = css`
+ background-color: ${colors.gray02};
+ border: 0;
+ border-radius: 5px;
+ flex-grow: 1;
+ padding: 0 10px;
+ margin: 0 10px;
+`;
+
+const commentStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+`;
+
+export default Detail;
diff --git a/src/pages/common/Following.tsx b/src/pages/common/Following.tsx
new file mode 100644
index 0000000..18188ec
--- /dev/null
+++ b/src/pages/common/Following.tsx
@@ -0,0 +1,191 @@
+import { useState } from 'react';
+import { css } from '@emotion/react';
+import { Plus } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import Button from '@/components/Button';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import Profile from '@/components/Profile';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useChannel from '@/hooks/useChannel';
+import useFollowingPlaylistFetch from '@/hooks/useFollowingPlaylist';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+const PAGE_SIZE = 5;
+
+const Following = () => {
+ const { userId } = useAuthStore();
+ const [selectedChannel, setSelectedChannel] = useState(userId);
+ const navigate = useNavigate();
+
+ const followingChannels = useChannel(userId, 'following');
+
+ const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
+ useFollowingPlaylistFetch(selectedChannel);
+ const playlists = data?.pages.flatMap((page) => page.playlistsData) || [];
+
+ const infiniteScrollRef = useInfiniteScroll(
+ async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ await fetchNextPage();
+ }
+ },
+ {
+ root: null,
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+ }
+ );
+
+ const onFollowingChannelClick = (channelId: string) => {
+ setSelectedChannel(selectedChannel === channelId ? userId : channelId);
+ };
+
+ const onToFollowingListPage = (userId: string) => {
+ navigate(`/profile/${userId}/following`);
+ };
+
+ return (
+
+ {followingChannels.length > 0 && (
+
+
+ {followingChannels.map((channel) => (
+
+
onFollowingChannelClick(channel.id)}
+ >
+
+
+
{channel.channelName}
+
+ ))}
+
+
onToFollowingListPage(userId)}
+ IconComponent={Plus}
+ color='primary'
+ shape='text'
+ size='lg'
+ />
+
+ )}
+
+ {playlists.length === 0 ? (
+
+
๋ชฉ๋ก์ด ๋น์์ด์
+
navigate(ROUTES.POPULAR)}
+ color='primary'
+ />
+
+ ) : (
+ <>
+ {playlists.map((playlist, index) => (
+
+ ))}
+
+ {isFetching && playlists.length >= PAGE_SIZE && (
+
+ )}
+
+
+ >
+ )}
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+`;
+
+const followingHeaderStyle = css`
+ display: flex;
+ padding: 4px 20px 20px 20px;
+ position: fixed;
+ z-index: 10;
+ background: ${colors.white};
+ width: 430px;
+ height: 100px;
+`;
+
+const followingListStyle = css`
+ flex-grow: 1;
+ display: flex;
+ gap: 6px;
+ overflow: hidden;
+
+ .profileContainer {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+`;
+
+const followingCoverStyle = (isSelected: boolean) => css`
+ border-radius: 50%;
+ border: ${isSelected
+ ? `1px solid ${colors.primaryNormal}`
+ : `1px solid ${colors.gray03}`};
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4px;
+ background: none;
+ cursor: pointer;
+`;
+
+const followingTextStyle = css`
+ font-size: ${fontSize.xs};
+ text-align: center;
+ margin-top: 2px;
+`;
+
+const playlistContainer = css`
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 100px 20px 20px 20px;
+`;
+
+const loadingSpinnerStyle = css`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const emptyContainer = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 60vh; /* ํ๋ฉด ๋์ด์ ๋ฐ๋ผ ์กฐ์ */
+ text-align: center;
+`;
+
+const emptyImage = css`
+ width: 150px; /* ์ ์ ํ ํฌ๊ธฐ๋ก ์กฐ์ */
+ height: auto;
+`;
+
+const emptyMessage = css`
+ margin: 20px 0;
+ font-size: ${fontSize.lg};
+ color: ${colors.gray04};
+`;
+
+export default Following;
diff --git a/src/pages/common/HashTag.tsx b/src/pages/common/HashTag.tsx
new file mode 100644
index 0000000..41cc253
--- /dev/null
+++ b/src/pages/common/HashTag.tsx
@@ -0,0 +1,121 @@
+import React, { useState } from 'react';
+import { css } from '@emotion/react';
+import { ChevronRight } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import HashTagComponent, { Tag } from '@/components/HashTag';
+import IconButton from '@/components/IconButton';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import { HASHTAGS } from '@/constants/hashtag';
+import ROUTES from '@/constants/route';
+import useTagSelection from '@/hooks/useTagSelection';
+import { updateFirstLogin, useAuthStore } from '@/stores/useAuthStore';
+
+const HashTag: React.FC = () => {
+ const [tags, setTags] = useState(HASHTAGS);
+ const { selectedTags, onTagSelection } = useTagSelection();
+ const navigate = useNavigate();
+
+ const { userId } = useAuthStore();
+
+ const onTagRemove = (label: string) => {
+ setTags(tags.filter((tag) => tag.label !== label));
+ onTagSelection(label, true);
+ };
+
+ const onTagClick = (label: string, removable: boolean) => {
+ onTagSelection(label, removable);
+ };
+
+ const onSkip = async () => {
+ await updateFirstLogin(userId, selectedTags);
+ useAuthStore.setState({ isFirstLogin: false });
+ navigate(ROUTES.ROOT);
+ };
+
+ return (
+
+
ํด์ํ๊ทธ
+
๊ด์ฌ ์๋ ํ๊ทธ๋ฅผ ์ ํํด์ฃผ์ธ์ (์ต๋ 10๊ฐ)
+
+
+
+
+
+ {selectedTags.length > 0 ? 'NEXT' : 'SKIP'}
+
+
+
+
+ );
+};
+
+const hashtagConainerStyle = css`
+ width: 100%;
+ max-width: 430px;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ margin: 0 auto;
+`;
+
+const titleStyle = css`
+ font-size: 32px;
+ margin-bottom: 20px;
+ font-weight: ${fontWeight.medium};
+`;
+
+const desStyle = css`
+ color: ${colors.primaryNormal};
+ font-size: ${fontSize.sm};
+ margin-bottom: 20px;
+`;
+
+const hashtagStyle = css`
+ margin: 0 44px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+`;
+
+const skipContainerStyle = css`
+ position: absolute;
+ width: 100%;
+ padding: 24px;
+ bottom: 0px;
+ right: 0px;
+ font-size: ${fontSize.lg};
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ background-color: transparent;
+ border: none;
+ border-top: 1px solid ${colors.gray02};
+`;
+
+const skipStyle = css`
+ cursor: pointer;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+
+ &:hover {
+ color: ${colors.primaryNormal};
+ }
+`;
+
+export default HashTag;
diff --git a/src/pages/common/Home.tsx b/src/pages/common/Home.tsx
new file mode 100644
index 0000000..8da87b3
--- /dev/null
+++ b/src/pages/common/Home.tsx
@@ -0,0 +1,107 @@
+import { useState } from 'react';
+import { css } from '@emotion/react';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import TextFilter from '@/components/TextFilter';
+import { fontSize } from '@/constants/font';
+import { useFetchHomePlaylists } from '@/hooks/useHomePlaylist';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+
+const PAGE_SIZE = 5;
+const SORT_OPTIONS = ['์ต์ ์', '์ข์์์', '๋๊ธ์'];
+const INFINITE_OPTIONS = {
+ root: null,
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+};
+
+const Home = () => {
+ const [selectedIndex, setSelectedIndex] = useState(0);
+ const selectedOption = SORT_OPTIONS[selectedIndex];
+
+ const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
+ useFetchHomePlaylists(selectedOption, PAGE_SIZE);
+
+ const playlists = data?.pages.flatMap((page) => page.playlistsData) || [];
+
+ const infiniteScrollRef = useInfiniteScroll(async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ }, INFINITE_OPTIONS);
+
+ return (
+
+
+
+
+
+ {playlists.length === 0 && !isFetching ? (
+
+
์ ๋ ฌ ์กฐ๊ฑด์ ํด๋นํ๋ ํ๋ฆฌ๊ฐ ์์ต๋๋ค.
+
+ ) : (
+ playlists.map((playlistItem) => (
+
+ ))
+ )}
+
{isFetching && }
+
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+ gap: 20px;
+`;
+
+const textFilterWrapperStyle = css`
+ display: flex;
+ flex-direction: row-reverse;
+`;
+
+const playlistWrapperStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+`;
+
+const emptyWrapperStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ padding-top: 80px;
+ text-align: center;
+
+ p {
+ font-size: ${fontSize.md};
+ line-height: 2.2rem;
+ }
+`;
+
+const loadingWrapperStyle = css`
+ display: flex;
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+`;
+
+export default Home;
diff --git a/src/pages/common/NotFound.tsx b/src/pages/common/NotFound.tsx
new file mode 100644
index 0000000..c3c6a87
--- /dev/null
+++ b/src/pages/common/NotFound.tsx
@@ -0,0 +1,58 @@
+import { css } from '@emotion/react';
+import { useNavigate } from 'react-router-dom';
+import Button from '@/components/Button';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+
+export const NotFound = () => {
+ const navigate = useNavigate();
+
+ const onBtnClick = (): void => {
+ navigate(ROUTES.ROOT);
+ };
+
+ return (
+
+
+ ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
+
+ ๋ฐฉ๋ฌธํ์๋ ค๋ ํ์ด์ง์ ์ฃผ์๊ฐ ์๋ชป ์
๋ ฅ๋์๊ฑฐ๋,
+ ํ์ด์ง์ ์ฃผ์๊ฐ ๋ณ๊ฒฝ ํน์ ์ญ์ ๋์ด
+ ์์ฒญํ์ ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
+
+
+
+ );
+};
+
+const containerStyles = css`
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ align-items: center;
+ text-align: center;
+ max-width: 430px;
+ height: 100%;
+ margin: 0 auto;
+ justify-content: center;
+ border-left: 1px solid ${colors.gray02};
+ border-right: 1px solid ${colors.gray02};
+
+ img {
+ width: 60%;
+ height: auto;
+ }
+
+ h1 {
+ font-size: ${fontSize.xxl};
+ font-weight: ${fontWeight.bold};
+ }
+
+ h3 {
+ font-size: ${fontSize.md};
+ color: ${colors.gray04};
+ line-height: 2.4rem;
+ }
+`;
diff --git a/src/pages/common/Popular.tsx b/src/pages/common/Popular.tsx
new file mode 100644
index 0000000..08ed39e
--- /dev/null
+++ b/src/pages/common/Popular.tsx
@@ -0,0 +1,125 @@
+import { useState } from 'react';
+import { css } from '@emotion/react';
+import Button from '@/components/Button';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import useGenerateTags from '@/hooks/useGenerateTags ';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import useTagFetch from '@/hooks/useTagFetch';
+
+export const Popular = () => {
+ const fixedTag = '#์ธ๊ธฐ ๊ธ์์น ๋์์';
+ const [clickedBtn, setClickedBtn] = useState(fixedTag);
+ const PAGE_SIZE = 5;
+ const tags = useGenerateTags(fixedTag);
+
+ const onButtonClick = (tag: string) => () => {
+ setClickedBtn(tag);
+ };
+
+ const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
+ useTagFetch(clickedBtn, PAGE_SIZE);
+ const playlists = data?.pages.flatMap((page) => page.playlistsData) || [];
+
+ const infiniteScrollRef = useInfiniteScroll(
+ async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ await fetchNextPage();
+ }
+ },
+ {
+ root: null,
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+ }
+ );
+
+ return (
+
+
+
+ {tags.map((tag) => (
+
+ ))}
+
+
{clickedBtn}
+
+
+ {playlists.map((playlistItem) => (
+
+ ))}
+
+ {isFetching && playlists.length >= PAGE_SIZE && }
+
+
+
+
+ );
+};
+
+const headerHeight = '157px';
+
+const contentContainerStyle = css`
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+`;
+
+const headerContainerStyle = css`
+ display: flex;
+ flex-direction: column;
+ padding: 10px;
+ position: fixed;
+ z-index: 10;
+ background: ${colors.white};
+ width: 430px;
+ gap: 20px;
+`;
+
+const tagContainerStyle = css`
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 5px;
+`;
+
+const titleContainerStyle = css`
+ align-items: center;
+ font-size: ${fontSize.xxl};
+ font-weight: ${fontWeight.medium};
+`;
+
+const playlistContainerStyle = css`
+ padding: calc(${headerHeight} + 20px) 20px 20px 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20px;
+ .css-1yx0w9a-largeCardStyles {
+ width: 100%;
+ }
+`;
+
+const loadingSpinnerStyle = css`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+export default Popular;
diff --git a/src/pages/common/Search.tsx b/src/pages/common/Search.tsx
new file mode 100644
index 0000000..e072c8f
--- /dev/null
+++ b/src/pages/common/Search.tsx
@@ -0,0 +1,93 @@
+import { css } from '@emotion/react';
+import { useParams } from 'react-router-dom';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import { fontSize } from '@/constants/font';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import { useFetchSearchPlaylists } from '@/hooks/useSearchPlaylist';
+
+const PAGE_SIZE = 5;
+const INFINITE_OPTIONS = {
+ root: null,
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+};
+
+const Search = () => {
+ const { keyword } = useParams();
+ const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
+ useFetchSearchPlaylists(keyword || '', PAGE_SIZE);
+
+ const playlists = data
+ ? data.pages.flatMap((page) => page.playlistsData)
+ : [];
+
+ const infiniteScrollRef = useInfiniteScroll(async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ }, INFINITE_OPTIONS);
+
+ return (
+
+
+ {playlists.length === 0 && !isFetching ? (
+
+
๊ฒ์ํ ๊ฒฐ๊ณผ์ ํด๋นํ๋ ํ๋ฆฌ๊ฐ ์์ต๋๋ค.
+
+ ) : (
+ playlists.map((playlistItem) => (
+
+ ))
+ )}
+
{isFetching && }
+
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+ gap: 20px;
+`;
+
+const playlistWrapperStyle = css`
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+`;
+
+const emptyWrapperStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ padding-top: 80px;
+ text-align: center;
+
+ p {
+ font-size: ${fontSize.md};
+ line-height: 2.2rem;
+ }
+`;
+
+const loadingWrapperStyle = css`
+ display: flex;
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+`;
+
+export default Search;
diff --git a/src/pages/common/SignIn.tsx b/src/pages/common/SignIn.tsx
new file mode 100644
index 0000000..a2031da
--- /dev/null
+++ b/src/pages/common/SignIn.tsx
@@ -0,0 +1,288 @@
+import { useState, useEffect } from 'react';
+import { css } from '@emotion/react';
+import {
+ signInWithEmailAndPassword,
+ GoogleAuthProvider,
+ signInWithPopup,
+} from 'firebase/auth';
+import { setDoc, doc, getDoc } from 'firebase/firestore';
+import { useNavigate } from 'react-router-dom';
+import google from '@/assets/google_icon.svg';
+import defaultProfile from '@/assets/profile_default.png';
+import Button from '@/components/Button';
+import InputBox from '@/components/InputBox';
+import Logo from '@/components/Logo';
+import Toast from '@/components/Toast';
+import Toggle from '@/components/Toggle';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import { auth, db } from '@/firebase/firbaseConfig';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+export const SignIn = () => {
+ const [id, setId] = useState('');
+ const [password, setPassword] = useState('');
+ const [isEmailRemembered, setIsEmailRemembered] = useState(false);
+ const [errorMessage, setErrorMessage] = useState('');
+ const navigate = useNavigate();
+
+ const setUser = useAuthStore((state) => state.setUser);
+ const getIsFirstLogin = useAuthStore((state) => state.getIsFirstLogin);
+ const fetchUserData = useAuthStore((state) => state.fetchUserData);
+
+ useEffect(() => {
+ const savedEmail = localStorage.getItem('savedEmail');
+ if (savedEmail) {
+ setId(savedEmail);
+ setIsEmailRemembered(true);
+ }
+ }, []);
+
+ const onToggleChange = (enabled: boolean) => {
+ setIsEmailRemembered(enabled);
+ if (enabled) {
+ localStorage.setItem('savedEmail', id);
+ } else {
+ localStorage.removeItem('savedEmail');
+ }
+ };
+
+ const onEmailLogin = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setErrorMessage('');
+ try {
+ const email = `${id}@gmail.com`;
+ const userCredential = await signInWithEmailAndPassword(
+ auth,
+ email,
+ password
+ );
+ setUser(userCredential.user);
+
+ fetchUserData(email);
+
+ setTimeout(() => {
+ const isFirstLogin = getIsFirstLogin();
+ if (isFirstLogin) {
+ navigate(ROUTES.HASH_TAG);
+ } else {
+ navigate(ROUTES.ROOT);
+ }
+ }, 100);
+ } catch (error) {
+ console.error('๋ก๊ทธ์ธ ์ค ์ค๋ฅ ๋ฐ์:', error);
+ setErrorMessage('์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํด์ฃผ์ธ์');
+ }
+ };
+
+ const onGoogleLogin = async () => {
+ const provider = new GoogleAuthProvider();
+ try {
+ const result = await signInWithPopup(auth, provider);
+ const { user } = result;
+ const userId = user.email?.split('@')[0];
+
+ const userDocRef = doc(db, 'users', `${userId}`);
+ const userDoc = await getDoc(userDocRef);
+
+ if (!userDoc.exists()) {
+ await setDoc(userDocRef, {
+ uid: user.uid,
+ channelName: user.displayName,
+ profileImg: '',
+ isFirstLogin: true,
+ likedPlaylist: [],
+ savedPlaylist: [],
+ channelFollower: [],
+ channelFollowing: [],
+ tags: [],
+ });
+ }
+
+ useAuthStore.setState({
+ user,
+ userId,
+ profileImage: user.photoURL || defaultProfile,
+ channelName: user?.displayName || '',
+ likedPlaylist: userDoc.exists() ? userDoc.data().likedPlaylist : [],
+ savedPlaylist: userDoc.exists() ? userDoc.data().savedPlaylist : [],
+ channelFollower: userDoc.exists() ? userDoc.data().channelFollower : [],
+ channelFollowing: userDoc.exists()
+ ? userDoc.data().channelFollowing
+ : [],
+ tags: userDoc.exists() ? userDoc.data().tags : [],
+ isFirstLogin: userDoc.exists() ? userDoc.data().isFirstLogin : true,
+ });
+
+ if (useAuthStore.getState().isFirstLogin) {
+ navigate(ROUTES.HASH_TAG);
+ } else {
+ navigate(ROUTES.ROOT);
+ }
+ } catch (error) {
+ setErrorMessage(
+ 'Google ๋ก๊ทธ์ธ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.'
+ );
+ }
+ };
+
+ const onSignUpMessage = (): void => {
+ navigate(ROUTES.SIGN_UP);
+ };
+
+ const validateId = (id: string) =>
+ id.length > 0 ? '' : '์์ด๋๋ฅผ ์
๋ ฅํด์ฃผ์ธ์';
+
+ const validatePassword = (value: string) =>
+ value.length > 0 ? '' : '๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํด์ฃผ์ธ์';
+
+ const isLoginDisabled = id.length === 0 || password.length === 0;
+
+ return (
+
+
+
+
+
+
+
+ ๊ณ์ ์ด ์์ผ์ ๊ฐ์?{' '}
+
+ ํ์๊ฐ์
ํ๊ธฐ
+
+
+
+
+
+ );
+};
+
+const containerStyle = css`
+ width: 100%;
+ max-width: 430px;
+ height: 100%;
+ margin: 0 auto;
+ /* display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center; */
+
+ &::before {
+ left: 50%;
+ transform: translateX(-215px);
+ }
+
+ &::after {
+ right: 50%;
+ transform: translateX(215px);
+ }
+
+ &::before,
+ &::after {
+ width: 1px;
+ position: fixed;
+ top: 0px;
+ bottom: 0px;
+ background-color: ${colors.gray02};
+ content: '';
+ z-index: 11;
+ }
+`;
+
+const formStyle = css`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+`;
+
+const toggleStyle = css`
+ width: 390px;
+ height: 20px;
+ margin: 6px 0 14px 0;
+ font-size: ${fontSize.sm};
+`;
+
+const loginBtnStyle = css`
+ width: 390px;
+ margin-bottom: 10px;
+`;
+
+const lineStyle = css`
+ width: 390px;
+ height: 0.5px;
+ background-color: ${colors.gray02};
+ margin: 20px 0;
+`;
+
+const signUpMessageStyle = css`
+ font-size: ${fontSize.sm};
+`;
+
+const signUpStyle = css`
+ color: ${colors.primaryNormal};
+ cursor: pointer;
+
+ &:hover {
+ font-weight: ${fontWeight.semiBold};
+ }
+`;
+
+const errorMessageStyle = css`
+ color: ${colors.redNormal};
+ font-size: ${fontSize.sm};
+ margin-bottom: 10px;
+ margin-top: -10px;
+`;
diff --git a/src/pages/common/SignUp.tsx b/src/pages/common/SignUp.tsx
new file mode 100644
index 0000000..648eddf
--- /dev/null
+++ b/src/pages/common/SignUp.tsx
@@ -0,0 +1,282 @@
+import { useState } from 'react';
+import { css } from '@emotion/react';
+import { createUserWithEmailAndPassword } from 'firebase/auth';
+import { doc, setDoc } from 'firebase/firestore';
+import { useNavigate } from 'react-router-dom';
+import Button from '@/components/Button';
+import Header from '@/components/Header';
+import InputBox from '@/components/InputBox';
+import Toast from '@/components/Toast';
+import colors from '@/constants/colors';
+import ROUTES from '@/constants/route';
+import { auth, db } from '@/firebase/firbaseConfig';
+import { useCheckDuplicate } from '@/hooks/useCheckDuplicate';
+import useToast from '@/hooks/useToast';
+import { checkIdExists, checkChannelNameExists } from '@/utils/checkDuplicate';
+
+export const SignUp = () => {
+ const [id, setId] = useState('');
+ const [password, setPassword] = useState('');
+ const [passwordConfirm, setPasswordConfirm] = useState('');
+ const [channelName, setChannelName] = useState('');
+
+ const {
+ message: idCheckMessage,
+ isChecked: isIdChecked,
+ checkDuplicate: checkIdDuplicate,
+ } = useCheckDuplicate();
+ const {
+ message: channelNameCheckMessage,
+ isChecked: isChannelNameChecked,
+ checkDuplicate: checkChannelNameDuplicate,
+ } = useCheckDuplicate();
+
+ const navigate = useNavigate();
+ const { toastTrigger } = useToast();
+
+ const onIdCheck = async () => {
+ const idValidationMessage = validateId(id);
+ if (idValidationMessage) {
+ checkIdDuplicate(
+ id,
+ () => Promise.resolve(false),
+ idValidationMessage,
+ ''
+ );
+ } else {
+ await checkIdDuplicate(
+ id,
+ checkIdExists,
+ '์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์์ด๋์
๋๋ค.',
+ '์ฌ์ฉ ๊ฐ๋ฅํ ์์ด๋์
๋๋ค.'
+ );
+ }
+ };
+
+ const onChannelNameCheck = async () => {
+ const channelNameValidation = validateChannelName(channelName);
+ if (channelNameValidation) {
+ checkChannelNameDuplicate(
+ channelName,
+ () => Promise.resolve(false),
+ channelNameValidation,
+ ''
+ );
+ } else {
+ await checkChannelNameDuplicate(
+ channelName.trim(),
+ checkChannelNameExists,
+ '์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์ฑ๋ ์ด๋ฆ์
๋๋ค.',
+ '์ฌ์ฉ ๊ฐ๋ฅํ ์ฑ๋ ์ด๋ฆ์
๋๋ค.'
+ );
+ }
+ };
+
+ const onChannelNameChange = (
+ e: React.ChangeEvent
+ ) => {
+ setChannelName(e.target.value);
+ checkChannelNameDuplicate('', () => Promise.resolve(false), '', '');
+ };
+
+ const onIdChange = (
+ e: React.ChangeEvent
+ ) => {
+ setId(e.target.value);
+ checkIdDuplicate('', () => Promise.resolve(false), '', '');
+ };
+
+ const onSignUp = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!isIdChecked || !isChannelNameChecked) {
+ toastTrigger('์ค๋ณต ๊ฒ์ฌ๋ฅผ ์งํํด์ฃผ์ธ์.', 'fail');
+ return;
+ }
+
+ const email = `${id}@gmail.com`;
+
+ const userCredential = await createUserWithEmailAndPassword(
+ auth,
+ email,
+ password
+ );
+ const { user } = userCredential;
+
+ await setDoc(doc(db, 'users', id), {
+ uid: user.uid,
+ channelName,
+ channelFollower: [],
+ channelFollowing: [],
+ savedPlaylist: [],
+ likedPlaylist: [],
+ profileImg: '',
+ tags: [],
+ isFirstLogin: true,
+ });
+
+ navigate(ROUTES.SIGN_IN);
+ toastTrigger('ํ์๊ฐ์
์ด ์๋ฃ๋์์ต๋๋ค! ๐ฅณ', 'success');
+ };
+
+ const validateId = (value: string) => {
+ if (value.length < 5) {
+ return '์์ด๋๋ 5์๋ฆฌ ์ด์์ด์ด์ผ ํฉ๋๋ค';
+ }
+ return '';
+ };
+
+ const validatePassword = (value: string) => {
+ const regex =
+ /^(?=.*[!@#$%^&*(),.?":{}|<>])[A-Za-z\d!@#$%^&*(),.?":{}|<>]{5,}$/;
+ if (!regex.test(value)) {
+ return '๋น๋ฐ๋ฒํธ๋ 6์๋ฆฌ ์ด์, ํน์๋ฌธ์(!,@,-,$,*)ํฌํจ์ด์ด์ผ ํฉ๋๋ค';
+ }
+ return '์ฌ์ฉ ๊ฐ๋ฅํ ๋น๋ฐ๋ฒํธ์
๋๋ค.';
+ };
+
+ const validatePasswordConfirm = (value: string) => {
+ if (value !== password) {
+ return '๋น๋ฐ๋ฒํธ๊ฐ ๋ค๋ฆ
๋๋ค';
+ }
+ return '๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํฉ๋๋ค.';
+ };
+
+ const validateChannelName = (value: string) => {
+ if (value.length < 2) {
+ return '์ฑ๋ ์ด๋ฆ์ 2์๋ฆฌ ์ด์์ด์ด์ผ ํฉ๋๋ค';
+ }
+ return '';
+ };
+
+ const isSignUpDisabled =
+ id.length < 5 ||
+ password.length < 6 ||
+ passwordConfirm.length < 6 ||
+ channelName.length < 2;
+
+ return (
+
+ );
+};
+
+const containerStyle = css`
+ width: 100%;
+ max-width: 430px;
+ margin: 0 auto;
+
+ &::before {
+ left: 50%;
+ transform: translateX(-215px);
+ }
+
+ &::after {
+ right: 50%;
+ transform: translateX(215px);
+ }
+
+ &::before,
+ &::after {
+ width: 1px;
+ position: fixed;
+ top: 0px;
+ bottom: 0px;
+ background-color: ${colors.gray02};
+ content: '';
+ z-index: 11;
+ }
+`;
+
+const signUpContainerStyle = css`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ padding: 20px;
+ gap: 4px;
+
+ .css-6gpx6l-inputContainerStyle {
+ height: 72px;
+ margin-bottom: 0;
+ }
+`;
+
+const duplicateStyle = css`
+ display: flex;
+ align-items: center;
+ position: relative;
+ width: 100%;
+ gap: 4px;
+
+ button {
+ height: 36px;
+ position: absolute;
+ right: 0;
+ top: 19px;
+ }
+`;
diff --git a/src/pages/playlist/PlayList.tsx b/src/pages/playlist/PlayList.tsx
new file mode 100644
index 0000000..c3bda36
--- /dev/null
+++ b/src/pages/playlist/PlayList.tsx
@@ -0,0 +1,130 @@
+import { css } from '@emotion/react';
+import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import { useChennelData } from '@/api/chennelInfo';
+import Button from '@/components/Button';
+import Profile from '@/components/Profile';
+import TabButton from '@/components/TabButton';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import { useFollowToggle } from '@/hooks/useFollowToggle';
+
+export const PlayList = () => {
+ const navigate = useNavigate();
+ const { userId } = useParams<{ userId: string }>();
+ const { chennelData } = useChennelData(userId);
+ const { isFollowing, handleFollowToggle } = useFollowToggle(userId);
+
+ const INFO_LIST = [
+ {
+ label: 'ํ๋ก์',
+ count: chennelData?.channelFollower.length.toString(),
+ onClick: () => {
+ navigate(ROUTES.PROFILE_FOLLOWER(userId));
+ },
+ },
+ {
+ label: 'ํ๋ก์',
+ count: chennelData?.channelFollowing.length.toString(),
+ onClick: () => {
+ navigate(ROUTES.PROFILE_FOLLOWING(userId));
+ },
+ },
+ ];
+
+ const TAB_NAMES = [
+ '๋ง์ดํ๋ฆฌ',
+ '์ ์ฅ๋ ํ๋ฆฌ',
+ ...(chennelData?.isMyChannel ? ['์ข์์'] : []),
+ ];
+
+ const TAB_LINKS = [
+ () => ROUTES.PLAYLIST(userId),
+ () => ROUTES.PLAYLIST_SAVED(userId),
+ ...(chennelData?.isMyChannel ? [() => ROUTES.PLAYLIST_LIKES(userId)] : []),
+ ];
+
+ return (
+ <>
+
+
+
+
+ {chennelData?.channelName}
+ {!chennelData?.isMyChannel && (
+
+ )}{' '}
+
+
+ {INFO_LIST.map((info, index) => (
+ -
+ {info.count}
+ {info.label}
+
+ ))}
+
+
+
+
+
+ >
+ );
+};
+
+const myInfoStyles = css`
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ padding: 20px;
+`;
+
+const profileStyles = css`
+ gap: 8px;
+ display: flex;
+ flex-direction: column;
+
+ .username {
+ font-size: ${fontSize.lg};
+ font-weight: ${fontWeight.medium};
+ display: flex;
+ align-items: center;
+
+ button {
+ margin-left: 8px;
+ }
+ }
+
+ .info-list {
+ display: flex;
+ gap: 24px;
+
+ li {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ :hover {
+ cursor: pointer;
+ }
+
+ span:nth-of-type(1) {
+ font-weight: ${fontWeight.semiBold};
+ font-size: ${fontSize.lg};
+ }
+
+ span:nth-of-type(2) {
+ color: ${colors.gray06};
+ font-size: ${fontSize.md};
+ }
+ }
+ }
+`;
diff --git a/src/pages/playlist/PlayListAdd.tsx b/src/pages/playlist/PlayListAdd.tsx
new file mode 100644
index 0000000..e5533ee
--- /dev/null
+++ b/src/pages/playlist/PlayListAdd.tsx
@@ -0,0 +1,509 @@
+import { useEffect, useRef, useState } from 'react';
+import { css } from '@emotion/react';
+import { Plus, SquarePlus, X } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import { addPlaylist } from '@/api/playlistInfo';
+import { getVideoInfo } from '@/api/video';
+import Button from '@/components/Button';
+import HashTag, { Tag } from '@/components/HashTag';
+import IconButton from '@/components/IconButton';
+import InputBox from '@/components/InputBox';
+import AddedVideo, { AddedLinkProps } from '@/components/playlist/AddedVideo';
+import PlaylistCard from '@/components/PlaylistCard';
+import Toggle from '@/components/Toggle';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import { TEXT } from '@/constants/playlist';
+import ROUTES from '@/constants/route';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { PlayListDataProps, ThumbnailProps } from '@/types/playlistType';
+import { tagging } from '@/utils/textUtils';
+import { getVideoId } from '@/utils/videoUtils';
+
+const INIT_VALUES: {
+ hashtag: string;
+ preview: PlayListDataProps;
+} = {
+ hashtag: '',
+ preview: {
+ title: '์์ ์ ๋ชฉ',
+ userId: '์์ ์ ์ ',
+ ownerChannelName: '',
+ tags: ['#์์1'],
+ likes: 0,
+ description: '์์ ์ค๋ช
',
+ isPublic: true,
+ playlistId: '์์ PL ID',
+ regDate: new Date().toISOString(),
+ thumbnail: '/src/assets/logoIcon.png',
+ links: [],
+ },
+};
+
+const PlayListAdd = () => {
+ const [enabled, setEnabled] = useState(true);
+ const [title, setTitle] = useState('');
+ const [desc, setDesc] = useState('');
+
+ const [link, setLink] = useState('');
+ const [videoList, setVideoList] = useState([]);
+
+ const [hashtag, setHashtag] = useState('');
+ const [addedHashtag, setAddedHashtag] = useState([]);
+
+ const [thumbnail, setThumbnail] = useState(null);
+ const fileInputRef = useRef(null);
+
+ const [preview, setPreview] = useState(
+ INIT_VALUES.preview
+ );
+
+ const { userId, channelName } = useAuthStore();
+ const { toastTrigger } = useToast();
+ const navigate = useNavigate();
+
+ const init = {
+ hashtag: () => {
+ setHashtag(INIT_VALUES.hashtag);
+ },
+ preview: () => {
+ setPreview(INIT_VALUES.preview);
+ },
+ };
+
+ const onKeydown = {
+ link: (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') onClick.addVideoLink();
+ },
+ hashtag: (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') onClick.addHashtag();
+ },
+ };
+
+ const validation = {
+ link: (value: string) => {
+ if (value.trim().length < 1) return '';
+ const videoId = getVideoId(value);
+ if (!!!videoId) return TEXT.link.validmsg;
+ if (videoId && check.dupl.link(videoId)) return TEXT.link.validDuplmsg;
+ return '';
+ },
+ hashtag: (value: string) => {
+ if (value.trim().length < 1 || value.trim() === '#') {
+ toastTrigger(TEXT.hashtag.required, 'fail');
+ return false;
+ }
+ if (addedHashtag.length >= 5) {
+ toastTrigger(TEXT.hashtag.limit, 'fail');
+ return false;
+ }
+ const taggedValue = tagging(value);
+ if (check.dupl.hashtag(taggedValue)) {
+ toastTrigger(TEXT.hashtag.validDuplmsg, 'fail');
+ return false;
+ }
+ return true;
+ },
+ };
+
+ const methods = {
+ onFileUpload: () => {
+ fileInputRef.current?.click();
+ },
+ createPreview: () => {
+ setPreview({
+ title,
+ userId: userId ?? '',
+ tags: addedHashtag.map((tag) => tag.label),
+ likes: 0,
+ description: desc,
+ isPublic: enabled,
+ regDate: new Date().toISOString(),
+ ownerChannelName: channelName,
+ thumbnail:
+ thumbnail?.preview ??
+ (videoList.length > 0
+ ? videoList[0].imgUrl
+ : INIT_VALUES.preview.thumbnail),
+ links: [...videoList.map((video) => video.link)],
+ });
+ },
+ };
+
+ const onClick = {
+ addVideoLink: async () => {
+ if (link.trim().length < 1) return;
+ if (validation.link(link)) {
+ return;
+ }
+ const { status, result } = await getVideoInfo(link);
+ if (status === 'fail' || !!!result) {
+ toastTrigger(TEXT.link.validmsg, 'fail');
+ return;
+ }
+
+ setVideoList([...videoList, result]);
+ setLink('');
+ },
+ removeVideoLink: (videoId: string) => {
+ setVideoList(videoList.filter((video) => video.videoId !== videoId));
+ },
+ removeThumbnail: () => {
+ setThumbnail(null);
+ if (fileInputRef.current) fileInputRef.current.value = '';
+ },
+ addHashtag: () => {
+ if (!!!validation.hashtag(hashtag)) return;
+ const taggedValue = tagging(hashtag);
+
+ setAddedHashtag([
+ ...addedHashtag,
+ {
+ id:
+ addedHashtag.length < 1
+ ? addedHashtag.length
+ : addedHashtag[addedHashtag.length - 1].id + 1,
+ label: taggedValue,
+ removable: true,
+ },
+ ]);
+ init.hashtag();
+ },
+ removeHashtag: (label: string) => {
+ setAddedHashtag(addedHashtag.filter((tag) => tag.label !== label));
+ },
+ createPlaylist: async () => {
+ const checkResult = check.required();
+ if (!!!checkResult.status) {
+ toastTrigger(checkResult.msg, 'fail');
+ return;
+ }
+
+ const playlist: PlayListDataProps = {
+ ...preview,
+ thumbnailFile: thumbnail?.file,
+ };
+
+ const response = await addPlaylist(playlist);
+
+ if (response.status === 'success') {
+ toastTrigger(TEXT.toast.create, 'success');
+ navigate(ROUTES.PLAYLIST(userId));
+ }
+ },
+ };
+
+ const check = {
+ required: (): {
+ status: boolean;
+ msg: string;
+ } => {
+ if (title.trim().length < 1)
+ return { status: false, msg: TEXT.title.required };
+ if (videoList.length < 1)
+ return { status: false, msg: TEXT.link.required };
+ if (addedHashtag.length < 1)
+ return {
+ status: false,
+ msg: TEXT.hashtag.required,
+ };
+
+ return { status: true, msg: '' };
+ },
+ dupl: {
+ hashtag: (inputTag: string): boolean => {
+ let result = false;
+
+ addedHashtag.map((hashtag) => {
+ if (hashtag.label === inputTag) return (result = true);
+ });
+ return result;
+ },
+ link: (videoId: string): boolean => {
+ let result = false;
+ videoList.map((video) => {
+ if (video.videoId.toString() === videoId) return (result = true);
+ });
+ return result;
+ },
+ },
+ };
+
+ const onChange = {
+ title: (e: React.ChangeEvent) => {
+ setTitle(e.target.value);
+ },
+ description: (
+ e: React.ChangeEvent
+ ) => {
+ setDesc(e.target.value);
+ },
+ link: (e: React.ChangeEvent) => {
+ setLink(e.target.value);
+ },
+ hashtag: (e: React.ChangeEvent) => {
+ setHashtag(e.target.value);
+ },
+ thumbnail: (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onloadend = () => {
+ setThumbnail({ file, preview: reader.result as string });
+ };
+ }
+ },
+ };
+
+ useEffect(() => {
+ methods.createPreview();
+ }, [enabled, title, addedHashtag, videoList, thumbnail]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {videoList.length > 0 && (
+
+ {videoList.map((video, index) => (
+
+
{}}
+ provider={video.provider}
+ />
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ {thumbnail && (
+
+ )}
+
+
+
+
+
+
+
+
+ {/* {enabled ? (
+
+ ) : (
+
+ )} */}
+
+
+
+
+ );
+};
+
+const btnHeight = '52px';
+const navHeight = '60px';
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px 20px calc(${navHeight} + ${btnHeight}) 20px;
+ gap: 16px;
+
+ .css-6gpx6l-inputContainerStyle {
+ margin-bottom: 0;
+ }
+`;
+
+const labelStyle = {
+ fontSize: `${fontSize.md}`,
+ fontWeight: `${fontWeight.semiBold}`,
+ paddingBottom: '4px',
+};
+
+const toggleStyle = css`
+ width: 100%;
+
+ & button {
+ padding-right: 5px;
+ }
+
+ & span {
+ align-self: center;
+ }
+`;
+
+const linkStyle = css`
+ display: flex;
+ align-self: center;
+ width: 100%;
+ height: 82px;
+`;
+
+const videoListStyle = css`
+ width: 100%;
+ border-radius: 8px;
+ border: 1px solid ${colors.gray02};
+ margin-top: 8px;
+ padding-bottom: 8px;
+`;
+
+const videoItemStyle = css`
+ display: flex;
+`;
+
+const hashtagDivStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
+const thumbnailStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ gap: 4px;
+
+ & label {
+ padding: 5px 0;
+ }
+`;
+
+const previewStyle = css`
+ width: 100%;
+ gap: 12px;
+ display: flex;
+ flex-direction: column;
+ margin-top: 12px;
+
+ .css-asypq0-smallCardStyles {
+ flex-direction: column;
+ gap: 4px;
+ font-size: ${fontSize.sm};
+ }
+`;
+
+const createButtonStyle = css`
+ position: fixed;
+ bottom: 60px;
+ background: ${colors.white};
+ padding: 10px;
+ width: 430px;
+`;
+
+const videoStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
+export default PlayListAdd;
diff --git a/src/pages/playlist/PlayListEdit.tsx b/src/pages/playlist/PlayListEdit.tsx
new file mode 100644
index 0000000..85080ce
--- /dev/null
+++ b/src/pages/playlist/PlayListEdit.tsx
@@ -0,0 +1,506 @@
+import { useEffect, useRef, useState } from 'react';
+import { css } from '@emotion/react';
+import { PlusSquare, SquarePlus, X } from 'lucide-react';
+import { useNavigate, useParams } from 'react-router-dom';
+import { getPlaylistInfo, updatePlaylist } from '@/api/playlistInfo';
+import { getVideoInfo } from '@/api/video';
+import Button from '@/components/Button';
+import HashTag, { Tag } from '@/components/HashTag';
+import IconButton from '@/components/IconButton';
+import InputBox from '@/components/InputBox';
+import AddedVideo, { AddedLinkProps } from '@/components/playlist/AddedVideo';
+import PlaylistCard from '@/components/PlaylistCard';
+import Toggle from '@/components/Toggle';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import { TEXT } from '@/constants/playlist';
+import ROUTES from '@/constants/route';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { PlayListDataProps, ThumbnailProps } from '@/types/playlistType';
+import { tagging } from '@/utils/textUtils';
+import { getVideoId } from '@/utils/videoUtils';
+
+const INIT_VALUES: {
+ hashtag: string;
+ preview: PlayListDataProps;
+} = {
+ hashtag: '',
+ preview: {
+ title: '์์ ์ ๋ชฉ',
+ userId: '์์ ์ ์ ',
+ ownerChannelName: '',
+ tags: ['#์์1'],
+ likes: 0,
+ description: '์์ ์ค๋ช
',
+ isPublic: true,
+ playlistId: '์์ PL ID',
+ regDate: new Date().toISOString(),
+ thumbnail: '/src/assets/logoIcon.png',
+ links: [],
+ },
+};
+
+const PlayListEdit = () => {
+ const navigate = useNavigate();
+ const { toastTrigger } = useToast();
+ const { userId, channelName } = useAuthStore();
+
+ const { playlistId } = useParams();
+ const [playlistInfo, setPlaylistInfo] = useState();
+
+ const [enabled, setEnabled] = useState(true);
+ const [title, setTitle] = useState('');
+ const [desc, setDesc] = useState('');
+
+ const [link, setLink] = useState('');
+ const [videoList, setVideoList] = useState([]);
+
+ const [hashtag, setHashtag] = useState('');
+ const [addedHashtag, setAddedHashtag] = useState([]);
+
+ const [prevThumbnail, setPrevThumbnail] = useState(null);
+ const [thumbnail, setThumbnail] = useState(null);
+ const fileInputRef = useRef(null);
+
+ const [preview, setPreview] = useState(
+ INIT_VALUES.preview
+ );
+
+ const init = {
+ all: async (values: PlayListDataProps) => {
+ setEnabled(values.isPublic);
+ setTitle(values.title);
+ setDesc(values.description ?? '');
+
+ const tempList: AddedLinkProps[] = new Array(values.links.length);
+
+ await Promise.all(
+ values.links.map(async (link, index) => {
+ const { result } = await getVideoInfo(link);
+ if (result) tempList[index] = result;
+ })
+ );
+ setVideoList(tempList);
+
+ values.tags.map(async (tag) => {
+ addedHashtag.push({
+ id:
+ addedHashtag.length < 1
+ ? addedHashtag.length
+ : addedHashtag[addedHashtag.length - 1].id + 1,
+ label: tag,
+ removable: true,
+ });
+ });
+
+ setPrevThumbnail(values.thumbnail);
+ },
+ hashtag: () => {
+ setHashtag(INIT_VALUES.hashtag);
+ },
+ preview: () => {
+ setPreview(INIT_VALUES.preview);
+ },
+ };
+
+ const onKeydown = {
+ link: (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') onClick.addVideoLink();
+ },
+ hashtag: (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') onClick.addHashtag();
+ },
+ };
+
+ const validation = {
+ link: (value: string) => {
+ if (value.trim().length < 1) return '';
+ const videoId = getVideoId(value);
+ if (!!!videoId) return TEXT.link.validmsg;
+ if (videoId && check.dupl.link(videoId)) return TEXT.link.validDuplmsg;
+ return '';
+ },
+ hashtag: (value: string) => {
+ if (value.trim().length < 1 || value.trim() === '#') {
+ toastTrigger(TEXT.hashtag.required, 'fail');
+ return false;
+ }
+ if (addedHashtag.length >= 5) {
+ toastTrigger(TEXT.hashtag.limit, 'fail');
+ return false;
+ }
+ const taggedValue = tagging(value);
+ if (check.dupl.hashtag(taggedValue)) {
+ toastTrigger(TEXT.hashtag.validDuplmsg, 'fail');
+ return false;
+ }
+ return true;
+ },
+ };
+
+ const methods = {
+ onFileUpload: () => {
+ fileInputRef.current?.click();
+ },
+ createPreview: () => {
+ setPreview({
+ title,
+ userId: userId ?? '',
+ tags: addedHashtag.map((tag) => tag.label),
+ likes: 0,
+ description: desc,
+ isPublic: enabled,
+ regDate: new Date().toISOString(),
+ ownerChannelName: channelName,
+ thumbnail:
+ thumbnail?.preview ??
+ prevThumbnail ??
+ (videoList.length > 0
+ ? videoList[0].imgUrl
+ : INIT_VALUES.preview.thumbnail),
+ links: [...videoList.map((video) => video.link)],
+ });
+ },
+ };
+
+ const onClick = {
+ addVideoLink: async () => {
+ if (link.trim().length < 1) return;
+ if (validation.link(link)) {
+ return;
+ }
+ const { status, result } = await getVideoInfo(link);
+ if (status === 'fail' || !!!result) {
+ toastTrigger(TEXT.link.validmsg, 'fail');
+ return;
+ }
+
+ setVideoList([...videoList, result]);
+ setLink('');
+ },
+ removeVideoLink: (videoId: string) => {
+ setVideoList(videoList.filter((video) => video.videoId !== videoId));
+ },
+ removeThumbnail: () => {
+ if (prevThumbnail) {
+ setPrevThumbnail(null);
+ return;
+ }
+ setThumbnail(null);
+ if (fileInputRef.current) fileInputRef.current.value = '';
+ },
+ addHashtag: () => {
+ if (!!!validation.hashtag(hashtag)) return;
+ const taggedValue = tagging(hashtag);
+
+ setAddedHashtag([
+ ...addedHashtag,
+ {
+ id:
+ addedHashtag.length < 1
+ ? addedHashtag.length
+ : addedHashtag[addedHashtag.length - 1].id + 1,
+ label: taggedValue,
+ removable: true,
+ },
+ ]);
+ init.hashtag();
+ },
+ removeHashtag: (label: string) => {
+ setAddedHashtag(addedHashtag.filter((tag) => tag.label !== label));
+ },
+ modifyPlaylist: async () => {
+ const checkResult = check.required();
+ if (!!!checkResult.status) {
+ toastTrigger(checkResult.msg, 'fail');
+ return;
+ }
+
+ const playlist: PlayListDataProps = {
+ ...preview,
+ thumbnailFile: thumbnail?.file,
+ };
+
+ const response = await updatePlaylist(playlist, Number(playlistId));
+
+ if (response.status === 'success') {
+ toastTrigger(TEXT.toast.modify, 'success');
+ navigate(ROUTES.PLAYLIST(userId));
+ }
+ },
+ };
+
+ const check = {
+ required: (): {
+ status: boolean;
+ msg: string;
+ } => {
+ if (title.trim().length < 1)
+ return { status: false, msg: TEXT.title.required };
+ if (videoList.length < 1)
+ return { status: false, msg: TEXT.link.required };
+ if (addedHashtag.length < 1)
+ return {
+ status: false,
+ msg: TEXT.hashtag.required,
+ };
+
+ return { status: true, msg: '' };
+ },
+ dupl: {
+ hashtag: (inputTag: string): boolean => {
+ let result = false;
+
+ addedHashtag.map((hashtag) => {
+ if (hashtag.label === inputTag) return (result = true);
+ });
+ return result;
+ },
+ link: (videoId: string): boolean => {
+ let result = false;
+ videoList.map((video) => {
+ if (video.videoId.toString() === videoId) return (result = true);
+ });
+ return result;
+ },
+ },
+ };
+
+ const onChange = {
+ title: (e: React.ChangeEvent) => {
+ setTitle(e.target.value);
+ },
+ description: (
+ e: React.ChangeEvent
+ ) => {
+ setDesc(e.target.value);
+ },
+ link: (e: React.ChangeEvent) => {
+ setLink(e.target.value);
+ },
+ hashtag: (e: React.ChangeEvent) => {
+ setHashtag(e.target.value);
+ },
+ thumbnail: (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onloadend = () => {
+ setThumbnail({ file, preview: reader.result as string });
+ };
+ }
+ },
+ };
+
+ useEffect(() => {
+ (async () => {
+ const data = await getPlaylistInfo(Number(playlistId));
+ if (data.result) setPlaylistInfo(data.result);
+ })();
+ }, []);
+
+ useEffect(() => {
+ if (playlistInfo) init.all(playlistInfo);
+ }, [playlistInfo]);
+
+ useEffect(() => {
+ methods.createPreview();
+ }, [enabled, title, addedHashtag, videoList, thumbnail, prevThumbnail]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {videoList.length > 0 && (
+
+ {videoList.map((video, index) => (
+
+
{}}
+ provider={video.provider}
+ />
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ {(prevThumbnail || thumbnail) && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ padding-bottom: 60px;
+
+ & > div {
+ padding: 5px 0;
+ width: 100%;
+ }
+`;
+
+const labelStyle = {
+ fontSize: `${fontSize.lg}`,
+ fontWeight: `${fontWeight.bold}`,
+};
+
+const toggleStyle = css`
+ width: 100%;
+
+ & button {
+ padding-right: 5px;
+ }
+
+ & span {
+ align-self: center;
+ }
+`;
+
+const linkStyle = css`
+ display: flex;
+ align-self: center;
+ width: 100%;
+`;
+
+const videoListStyle = css`
+ /* width: 100%; */
+ /* overflow-y: auto;
+ overflow-x: hidden; */
+`;
+
+const videoItemStyle = css`
+ display: flex;
+`;
+
+const hashtagDivStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
+const thumbnailStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+
+ & label {
+ padding: 5px 0;
+ }
+`;
+
+const previewStyle = css`
+ width: 100%;
+ & > * {
+ padding: 10px 0;
+ }
+`;
+
+export default PlayListEdit;
diff --git a/src/pages/playlist/PlayListHome.tsx b/src/pages/playlist/PlayListHome.tsx
new file mode 100644
index 0000000..fa7278a
--- /dev/null
+++ b/src/pages/playlist/PlayListHome.tsx
@@ -0,0 +1,196 @@
+import { useEffect, useMemo, useState } from 'react';
+import { css } from '@emotion/react';
+import { Plus } from 'lucide-react';
+import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import { useChennelData } from '@/api/chennelInfo';
+import { useFetchUserPlaylist } from '@/api/myplaylists';
+import Button from '@/components/Button';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import PopupFilter from '@/components/PopupFilter';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import { PlayListDataProps } from '@/types/playlistType';
+import {
+ filterPlaylistByVisibility,
+ sortPlaylistsByOption,
+} from '@/utils/filterUtils';
+
+const PAGE_SIZE = 5;
+
+export const PlayListHome = () => {
+ const navigate = useNavigate();
+ const { userId } = useParams<{ userId: string }>();
+ const { chennelData } = useChennelData(userId);
+
+ const [filterOptions, setFilterOptions] = useState([0, 0]);
+ const [localPlaylists, setLocalPlaylists] = useState([]);
+
+ const optionGroups = [
+ { label: '์ ๋ ฌ', options: ['์ต์ ์', '์ข์์์', '๋๊ธ์'] },
+ { label: '๊ณต๊ฐ์ฌ๋ถ', options: ['์ ์ฒด', '๊ณต๊ฐ', '๋น๊ณต๊ฐ'] },
+ ];
+
+ const { data, fetchNextPage, hasNextPage, isFetching, refetch } =
+ useFetchUserPlaylist(PAGE_SIZE);
+
+ useEffect(() => {
+ if (data) {
+ setLocalPlaylists(data.pages.flatMap((page) => page.playlist));
+ }
+ }, [data]);
+
+ const infiniteScrollRef = useInfiniteScroll(
+ async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetching) {
+ fetchNextPage();
+ }
+ },
+ {
+ root: document.querySelector('.scroll-container'),
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+ }
+ );
+
+ const filteredPlaylist = useMemo(() => {
+ const filteredPlaylist = filterPlaylistByVisibility(
+ localPlaylists,
+ filterOptions[1]
+ );
+ return sortPlaylistsByOption(filteredPlaylist, filterOptions[0]);
+ }, [localPlaylists, filterOptions]);
+
+ const onPlaylistDelete = (deletedPlaylistId: number) => {
+ setLocalPlaylists((prevPlaylists) =>
+ prevPlaylists.filter(
+ (playlist) => Number(playlist.playlistId) !== deletedPlaylistId
+ )
+ );
+
+ refetch();
+ };
+
+ return (
+
+
+
+ {chennelData?.isMyChannel && (
+
{
+ navigate(ROUTES.PLAYLIST_ADD(userId));
+ }}
+ />
+ )}
+
+ {filteredPlaylist.length > 0 && (
+ ์ด {filteredPlaylist.length}๊ฐ์ ํ๋ฆฌ
+ )}
+ {isFetching ? (
+
+
+
+ ) : filteredPlaylist.length === 0 ? (
+
+
์์ง ํ๋ฆฌ๊ฐ ์๋ค์.
+
๋๋ง์ ํ๋ฆฌ๋ก ์ฑ์๋ณผ๊น์?
+
+ ) : (
+
+ {filteredPlaylist.map((playlistItem) => (
+ -
+
+
+ ))}
+
+
+ )}
+
+
+ );
+};
+
+const profileHeight = '140px';
+const tabHeight = '41px';
+
+const homeContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 12px 20px 0 20px;
+ height: calc(100% - ${profileHeight} - ${tabHeight});
+
+ .filter-area {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ p {
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ }
+`;
+
+const cardContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ overflow-y: auto;
+ padding-bottom: 40px;
+ align-items: center;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+
+ .list {
+ width: 100%;
+ }
+`;
+
+const emptyStateStyles = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ padding-top: 80px;
+ text-align: center;
+
+ p {
+ font-size: ${fontSize.md};
+ line-height: 2.2rem;
+ }
+
+ img {
+ width: 50%;
+ }
+`;
+
+const loadingStyless = css`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
diff --git a/src/pages/playlist/PlayListLikes.tsx b/src/pages/playlist/PlayListLikes.tsx
new file mode 100644
index 0000000..dcb6f88
--- /dev/null
+++ b/src/pages/playlist/PlayListLikes.tsx
@@ -0,0 +1,179 @@
+import { useMemo, useState } from 'react';
+import { css } from '@emotion/react';
+import { Plus } from 'lucide-react';
+import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import { useChennelData } from '@/api/chennelInfo';
+import { useFetchLikedPlaylist } from '@/api/myplaylists';
+import Button from '@/components/Button';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import PopupFilter from '@/components/PopupFilter';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import { sortPlaylistsByOption } from '@/utils/filterUtils';
+
+const PAGE_SIZE = 5;
+
+export const PlayListLikes = () => {
+ const navigate = useNavigate();
+ const { userId } = useParams<{ userId: string }>();
+ const { chennelData } = useChennelData(userId);
+
+ const [filterOptions, setFilterOptions] = useState([0]);
+
+ const optionGroups = [
+ { label: '์ ๋ ฌ', options: ['์ต์ ์', '์ข์์์', '๋๊ธ์'] },
+ ];
+
+ const { data, fetchNextPage, hasNextPage, isFetching } =
+ useFetchLikedPlaylist(PAGE_SIZE);
+
+ const playlist = useMemo(
+ () => (data ? data.pages.flatMap((page) => page.playlist) : []),
+ [data]
+ );
+
+ const infiniteScrollRef = useInfiniteScroll(
+ async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetching) {
+ fetchNextPage();
+ }
+ },
+ {
+ root: document.querySelector('.scroll-container'),
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+ }
+ );
+
+ const sortedPlaylist = useMemo(
+ () => sortPlaylistsByOption(playlist, filterOptions[0]),
+ [playlist, filterOptions]
+ );
+
+ return (
+ <>
+
+
+
+ {chennelData?.isMyChannel && (
+
{
+ navigate(ROUTES.PLAYLIST_ADD());
+ }}
+ />
+ )}
+
+ {sortedPlaylist.length > 0 && (
+ ์ด {sortedPlaylist.length}๊ฐ์ ํ๋ฆฌ
+ )}
+ {isFetching ? (
+
+
+
+ ) : sortedPlaylist.length === 0 ? (
+
+
์์ง ์ข์์ํ ํ๋ฆฌ๊ฐ ์๋ค์.
+
์ธ๊ธฐ ํ๋ฆฌ๋ก ์ฑ์๋ณผ๊น์?
+
navigate(ROUTES.POPULAR)}
+ />
+
+ ) : (
+
+ {sortedPlaylist.map((playlistItem, index) => (
+ -
+
+
+ ))}
+
+
+ )}
+
+
+ >
+ );
+};
+
+const profileHeight = '140px';
+const tabHeight = '41px';
+
+const homeContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 12px 20px 0 20px;
+ height: calc(100% - ${profileHeight} - ${tabHeight});
+
+ .filter-area {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ p {
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ }
+`;
+
+const cardContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ overflow-y: auto;
+ padding-bottom: 40px;
+ align-items: center;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+
+ .list {
+ width: 100%;
+ }
+`;
+
+const emptyStateStyles = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ padding-top: 80px;
+
+ p {
+ font-size: ${fontSize.md};
+ line-height: 2.2rem;
+ }
+
+ img {
+ width: 50%;
+ }
+`;
+
+const loadingStyless = css`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
diff --git a/src/pages/playlist/PlayListSaved.tsx b/src/pages/playlist/PlayListSaved.tsx
new file mode 100644
index 0000000..fc47b6d
--- /dev/null
+++ b/src/pages/playlist/PlayListSaved.tsx
@@ -0,0 +1,179 @@
+import { useMemo, useState } from 'react';
+import { css } from '@emotion/react';
+import { Plus } from 'lucide-react';
+import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import { useChennelData } from '@/api/chennelInfo';
+import { useFetchSavedPlaylist } from '@/api/myplaylists';
+import Button from '@/components/Button';
+import LoadingSpinner from '@/components/LoadingSpinner';
+import PlaylistCard from '@/components/PlaylistCard';
+import PopupFilter from '@/components/PopupFilter';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import { sortPlaylistsByOption } from '@/utils/filterUtils';
+
+const PAGE_SIZE = 5;
+
+export const PlayListSaved = () => {
+ const navigate = useNavigate();
+ const { userId } = useParams<{ userId: string }>();
+ const { chennelData } = useChennelData(userId);
+
+ const [filterOptions, setFilterOptions] = useState([0]);
+
+ const optionGroups = [
+ { label: '์ ๋ ฌ', options: ['์ต์ ์', '์ข์์์', '๋๊ธ์'] },
+ ];
+
+ const { data, fetchNextPage, hasNextPage, isFetching } =
+ useFetchSavedPlaylist(PAGE_SIZE);
+
+ const playlist = useMemo(
+ () => (data ? data.pages.flatMap((page) => page.playlist) : []),
+ [data]
+ );
+
+ const infiniteScrollRef = useInfiniteScroll(
+ async (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetching) {
+ fetchNextPage();
+ }
+ },
+ {
+ root: document.querySelector('.scroll-container'),
+ rootMargin: '0px 0px -20px 0px',
+ threshold: 0.5,
+ }
+ );
+
+ const sortedPlaylist = useMemo(
+ () => sortPlaylistsByOption(playlist, filterOptions[0]),
+ [playlist, filterOptions]
+ );
+
+ return (
+ <>
+
+
+
+ {chennelData?.isMyChannel && (
+
{
+ navigate(ROUTES.PLAYLIST_ADD(userId));
+ }}
+ />
+ )}
+
+ {sortedPlaylist.length > 0 && (
+ ์ด {sortedPlaylist.length}๊ฐ์ ํ๋ฆฌ
+ )}
+ {isFetching ? (
+
+
+
+ ) : sortedPlaylist.length === 0 ? (
+
+
์์ง ์ ์ฅํ ํ๋ฆฌ๊ฐ ์๋ค์.
+
์ธ๊ธฐ ํ๋ฆฌ๋ก ์ฑ์๋ณผ๊น์?
+
navigate(ROUTES.POPULAR)}
+ />
+
+ ) : (
+
+ {sortedPlaylist.map((playlistItem, index) => (
+ -
+
+
+ ))}
+
+
+ )}
+
+
+ >
+ );
+};
+
+const profileHeight = '140px';
+const tabHeight = '41px';
+
+const homeContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 12px 20px 0 20px;
+ height: calc(100% - ${profileHeight} - ${tabHeight});
+
+ .filter-area {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ p {
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ }
+`;
+
+const cardContainerStyles = css`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ overflow-y: auto;
+ padding-bottom: 40px;
+ align-items: center;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+
+ .list {
+ width: 100%;
+ }
+`;
+
+const emptyStateStyles = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ padding-top: 80px;
+
+ p {
+ font-size: ${fontSize.md};
+ line-height: 2.2rem;
+ }
+
+ img {
+ width: 50%;
+ }
+`;
+
+const loadingStyless = css`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
diff --git a/src/pages/profile/Profile.tsx b/src/pages/profile/Profile.tsx
new file mode 100644
index 0000000..f67ff70
--- /dev/null
+++ b/src/pages/profile/Profile.tsx
@@ -0,0 +1,7 @@
+import { Outlet } from 'react-router-dom';
+
+export const Profile = () => (
+ <>
+
+ >
+);
diff --git a/src/pages/profile/ProfileFollower.tsx b/src/pages/profile/ProfileFollower.tsx
new file mode 100644
index 0000000..cf36b21
--- /dev/null
+++ b/src/pages/profile/ProfileFollower.tsx
@@ -0,0 +1,93 @@
+import { css } from '@emotion/react';
+import { UserMinus } from 'lucide-react';
+import { useParams, useNavigate } from 'react-router-dom';
+import IconButton from '@/components/IconButton';
+import Modal from '@/components/Modal';
+import Profile from '@/components/Profile';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useList from '@/hooks/useList';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+const FollowerList = () => {
+ const { userId } = useParams();
+ const { userId: loggedInUserId } = useAuthStore();
+ const isOwner = userId === loggedInUserId;
+ const { list: followerList, handleUserMinusClick } = useList(
+ userId || '',
+ 'follower'
+ );
+ const navigate = useNavigate();
+ const onToChannel = (goToId: string): void => {
+ navigate(ROUTES.PLAYLIST(goToId));
+ };
+
+ return (
+ <>
+
+
+ {followerList.length === 0
+ ? 'ํ๋ก์๊ฐ ์์ต๋๋ค' // ํ๋ก์ ๋ฆฌ์คํธ๊ฐ ๋น์ด ์์ ๊ฒฝ์ฐ
+ : `์ด ${followerList.length} ๋ช
`}{' '}
+ {/* ํ๋ก์ ๋ฆฌ์คํธ๊ฐ ์์ ๊ฒฝ์ฐ */}
+
+
+ {followerList.length > 0 &&
+ followerList.map((data, index) => (
+
+
+ onToChannel(data.name)}
+ />
+ {data.channelName}
+
+
+ {isOwner && (
+
handleUserMinusClick(data.name)}
+ />
+ )}
+
+ ))}
+
+
+
+ >
+ );
+};
+
+const rootContainer = css`
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+ gap: 8px;
+`;
+
+const numberingContainer = css`
+ font-size: ${fontSize.md};
+`;
+
+const profileListContainer = css`
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 60px;
+`;
+
+const profileItem = css`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 0;
+`;
+
+const profileContainerStyle = css`
+ display: flex;
+ align-items: center;
+ gap: 10px;
+`;
+
+export default FollowerList;
diff --git a/src/pages/profile/ProfileFollowing.tsx b/src/pages/profile/ProfileFollowing.tsx
new file mode 100644
index 0000000..6e3c69d
--- /dev/null
+++ b/src/pages/profile/ProfileFollowing.tsx
@@ -0,0 +1,115 @@
+import { css } from '@emotion/react';
+import { UserMinus } from 'lucide-react';
+import { useParams, useNavigate } from 'react-router-dom';
+import IconButton from '@/components/IconButton';
+import Modal from '@/components/Modal';
+import Profile from '@/components/Profile';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import useList from '@/hooks/useList';
+import { useAuthStore } from '@/stores/useAuthStore';
+import useModalStore from '@/stores/useModalStore';
+
+const FollowingList = () => {
+ const { userId } = useParams();
+ const { userId: loggedInUserId } = useAuthStore();
+ const isOwner = userId === loggedInUserId;
+ const { list: followingList, setList } = useList(userId || '', 'following');
+ const { removeFollowing } = useAuthStore.getState();
+ const { openModal } = useModalStore();
+
+ const navigate = useNavigate();
+ const onToChannel = (goToId: string): void => {
+ navigate(ROUTES.PLAYLIST(goToId));
+ };
+
+ const onUnfollow = async (removeId: string) => {
+ if (userId) {
+ try {
+ await removeFollowing(userId, removeId);
+ setList((prevList) =>
+ prevList.filter((user) => user.name !== removeId)
+ );
+ } catch (error) {
+ console.error('Error unfollowing user:', error);
+ }
+ }
+ };
+
+ const onserMinusClick = (removeId: string) => {
+ openModal({
+ type: 'confirm',
+ title: '์ธํ๋ก์ฐ ํ์ธ',
+ content: '์ ๋ง๋ก ์ด ์ ์ ๋ฅผ ์ธํ๋ก์ฐ ํ์๊ฒ ์ต๋๊น?',
+ onAction: () => onUnfollow(removeId),
+ });
+ };
+
+ return (
+ <>
+
+
+ {followingList.length === 0
+ ? 'ํ๋ก์ฐ์ค์ธ ์ฑ๋์ด ์์ต๋๋ค'
+ : `์ด ${followingList.length} ๋ช
`}{' '}
+
+
+ {followingList.length > 0 &&
+ followingList.map((data, index) => (
+
+
+ onToChannel(data.name)}
+ />
+ {data.channelName}
+
+
+ {isOwner && (
+
onserMinusClick(data.name)}
+ />
+ )}
+
+ ))}
+
+
+
+ >
+ );
+};
+
+const rootContainer = css`
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+ gap: 8px;
+`;
+
+const numberingContainer = css`
+ font-size: ${fontSize.md};
+`;
+
+const profileListContainer = css`
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 60px;
+`;
+
+const profileItem = css`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 0;
+`;
+
+const profileContainerStyle = css`
+ display: flex;
+ align-items: center;
+ gap: 10px;
+`;
+
+export default FollowingList;
diff --git a/src/pages/profile/ProfileHome.tsx b/src/pages/profile/ProfileHome.tsx
new file mode 100644
index 0000000..e74e963
--- /dev/null
+++ b/src/pages/profile/ProfileHome.tsx
@@ -0,0 +1,373 @@
+import { useState, useEffect } from 'react';
+import { css } from '@emotion/react';
+import { signOut } from 'firebase/auth';
+import { doc, deleteDoc } from 'firebase/firestore';
+import { Trash2, LogOut } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import {
+ getUserComments,
+ getPlaylistTitle,
+ getMyPlaylistCount,
+} from '@/api/profileInfo';
+import Button from '@/components/Button';
+import CheckBox from '@/components/CheckBox';
+import IconButton from '@/components/IconButton';
+import Modal from '@/components/Modal';
+import Profile from '@/components/Profile';
+import Toast from '@/components/Toast';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import { auth, db } from '@/firebase/firbaseConfig';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import useModalStore from '@/stores/useModalStore';
+
+interface Comment {
+ id: string;
+ content: string;
+ playlistId: number;
+ playlistTitle: string;
+}
+
+export const ProfileHome = () => {
+ const [comments, setComments] = useState([]);
+ const [myPlaylistCount, setMyPlaylistCount] = useState(0);
+ const [commentsPlus, setCommentsPlus] = useState(7);
+ const [checkedComments, setCheckedComments] = useState([]);
+ const [allChecked, setAllChecked] = useState(false);
+
+ const { openModal } = useModalStore();
+ const { toastTrigger } = useToast();
+ const navigate = useNavigate();
+
+ const {
+ userId,
+ profileImage,
+ channelName,
+ channelFollower,
+ channelFollowing,
+ clearUser,
+ } = useAuthStore();
+
+ useEffect(() => {
+ const fetchComments = async () => {
+ const userComments = await getUserComments(userId);
+
+ const commentWithTitle = await Promise.all(
+ userComments.map(async (comment) => {
+ const playlistTitle = await getPlaylistTitle(comment.playlistId);
+ return {
+ id: comment.commentsId,
+ content: comment.content,
+ playlistId: comment.playlistId,
+ playlistTitle,
+ };
+ })
+ );
+ const playlistCount = await getMyPlaylistCount(userId);
+ setMyPlaylistCount(playlistCount ?? 0);
+ setComments(commentWithTitle);
+ };
+
+ fetchComments();
+ }, []);
+
+ const deleteComment = async (commentId: string) => {
+ const commentRef = doc(db, 'comments', commentId);
+ await deleteDoc(commentRef);
+ toastTrigger('๋๊ธ ์ญ์ ๊ฐ ์๋ฃ๋์์ต๋๋ค.', 'success');
+ };
+
+ const commentSelection = (commentId: string, isChecked: boolean) => {
+ setCheckedComments((prev) => {
+ if (isChecked) {
+ return [...prev, commentId];
+ } else {
+ return prev.filter((id) => id !== commentId);
+ }
+ });
+ };
+
+ const deleteSelectedComments = async () => {
+ await Promise.all(
+ checkedComments.map((commentId) => deleteComment(commentId))
+ );
+ setComments((prev) =>
+ prev.filter((comment) => !checkedComments.includes(comment.id))
+ );
+ setCheckedComments([]);
+ };
+
+ const onCommentsPlus = () => {
+ setCommentsPlus((prev) => prev + 5);
+ };
+
+ const onAllComments = () => {
+ if (allChecked) {
+ setCheckedComments([]);
+ } else {
+ const visibleComment = comments
+ .slice(0, commentsPlus)
+ .map((comment) => comment.id);
+
+ setCheckedComments(visibleComment);
+ }
+ setAllChecked(!allChecked);
+ };
+
+ const logout = async () => {
+ await signOut(auth).then(() => {
+ clearUser();
+ navigate(ROUTES.ROOT);
+ });
+ };
+
+ const onDeleteButtonClick = () => {
+ openModal({
+ type: 'delete',
+ title: '๋๊ธ ์ญ์ ',
+ content: '๋๊ธ์ ์ญ์ ํ์๊ฒ ์ต๋๊น?',
+ onAction: deleteSelectedComments,
+ });
+ };
+
+ return (
+
+
+
+
{channelName}
+
navigate(ROUTES.PROFILE_MODIFY(userId))}
+ >
+ ํ๋กํ ์์
+
+
+
navigate(ROUTES.PLAYLIST(userId))}
+ >
+
{myPlaylistCount}
+
ํ๋ ์ด๋ฆฌ์คํธ
+
+
navigate(ROUTES.PROFILE_FOLLOWER(userId))}
+ >
+
{channelFollower.length}
+
ํ๋ก์
+
+
navigate(ROUTES.PROFILE_FOLLOWING(userId))}
+ >
+
{channelFollowing.length}
+
ํ๋ก์
+
+
+
+
+
๋ด๊ฐ ์ด ๋๊ธ {comments.length}
+ {comments.length === 0 ? (
+
๋๊ธ ๋ชฉ๋ก์ด ์์ด์.
+ ) : (
+ <>
+
+
+ {allChecked ? '์ ์ฒด ํด์ ' : '์ ์ฒด ์ ํ'}
+
+
+
+
+ {comments
+ .slice(0, commentsPlus)
+ .map(({ id, playlistTitle, content }, index) => (
+ -
+ commentSelection(id, isChecked)}
+ />
+
+
+
{playlistTitle}
+
{content}
+
+
+ ))}
+
+ {comments.length > commentsPlus && (
+
+ )}
+ >
+ )}
+
+
+
+ ๋ก๊ทธ์์
+
+
+
+
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+`;
+
+const profileContainerStyle = css`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ border-bottom: 1px solid ${colors.gray02};
+ margin-bottom: 20px;
+`;
+
+const profileNameStyle = css`
+ margin: 6px 0;
+ font-size: ${fontSize.xl};
+ color: ${colors.black};
+`;
+
+const profileBtnStyle = css`
+ background-color: ${colors.gray06};
+ border-radius: 50px;
+ font-size: ${fontSize.xs};
+ color: ${colors.white};
+ border: 1px solid ${colors.gray03};
+ padding: 4px 12px;
+ margin: 6px 0 20px 0;
+ cursor: pointer;
+`;
+
+const infoContainerStyle = css`
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+const infoStyle = css`
+ display: flex;
+ flex-direction: column;
+ width: 112px;
+ text-align: center;
+ cursor: pointer;
+
+ & > div:first-of-type {
+ font-weight: ${fontWeight.semiBold};
+ height: 22px;
+ }
+`;
+
+const commentContainerStyle = css`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: 0 20px;
+`;
+
+const commentHeaderStyle = css`
+ font-weight: ${fontWeight.medium};
+`;
+
+const deleteContainerStyle = css`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 16px;
+`;
+
+const allSelectBtnStyle = css`
+ background-color: ${colors.white};
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ padding: 4px 12px;
+ border-radius: 6px;
+ border: 1px solid ${colors.gray03};
+ cursor: pointer;
+`;
+
+const commentSelectStyle = css`
+ width: 100%;
+ margin-bottom: 12px;
+`;
+
+const commentStyle = css`
+ display: flex;
+ align-items: flex-start;
+ padding: 12px 0;
+ font-size: ${fontSize.sm};
+ border-bottom: 1.5px solid ${colors.gray02};
+
+ .css-13eeqks-checkBoxContainer {
+ margin-top: 7px;
+ }
+
+ & > *:not(:last-child) {
+ margin-right: 12px;
+ }
+
+ span {
+ color: ${colors.gray05};
+ margin-bottom: 6px;
+ }
+`;
+
+const commentDesStyle = css`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ line-height: 1.2;
+ flex: 1;
+ word-break: break-word;
+`;
+
+const logoutStyle = css`
+ width: 100%;
+ background-color: transparent;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 20px;
+ border: none;
+ border-top: 1px solid ${colors.gray02};
+ font-size: ${fontSize.sm};
+ color: ${colors.gray05};
+ padding: 14px;
+ cursor: pointer;
+ margin-bottom: 75px;
+`;
+
+const emptyCommentStyle = css`
+ height: calc(100vh - 520px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: ${colors.gray05};
+ font-size: ${fontSize.md};
+`;
diff --git a/src/pages/profile/ProfileUpdate.tsx b/src/pages/profile/ProfileUpdate.tsx
new file mode 100644
index 0000000..c5b64c5
--- /dev/null
+++ b/src/pages/profile/ProfileUpdate.tsx
@@ -0,0 +1,282 @@
+import React, { useState, useEffect } from 'react';
+import { css } from '@emotion/react';
+import { doc, setDoc, getDoc } from 'firebase/firestore';
+import { Camera } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import {
+ updateProfileImage,
+ getMyHashtag,
+ updateProfileTags,
+} from '@/api/profileInfo';
+import Button from '@/components/Button';
+import HashTag from '@/components/HashTag';
+import InputBox from '@/components/InputBox';
+import Modal from '@/components/Modal';
+import Profile from '@/components/Profile';
+import colors from '@/constants/colors';
+import { fontSize } from '@/constants/font';
+import ROUTES from '@/constants/route';
+import { db } from '@/firebase/firbaseConfig';
+import { useCheckDuplicate } from '@/hooks/useCheckDuplicate';
+import { useHashtagManage } from '@/hooks/useHashtagManage';
+import useToast from '@/hooks/useToast';
+import { useAuthStore } from '@/stores/useAuthStore';
+import { checkChannelNameExists } from '@/utils/checkDuplicate';
+
+export const ProfileUpdate = () => {
+ const { profileImage, channelName, userId } = useAuthStore();
+ const [selectedImage, setSelectedImage] = useState(null);
+ const [previewUrl, setPreviewUrl] = useState(profileImage);
+ const [newChannelName, setNewChannelName] = useState(channelName);
+
+ const {
+ message: channelNameCheckMessage,
+ isChecked: isChannelNameChecked,
+ checkDuplicate: checkChannelNameDuplicate,
+ setMessage: setChannelNameCheckMessage,
+ setIsChecked: setIsChannelNameChecked,
+ } = useCheckDuplicate();
+
+ const {
+ hashtags,
+ hashtag,
+ setHashtags,
+ setHashtag,
+ addHashtag,
+ removeHashtag,
+ } = useHashtagManage();
+
+ const navigate = useNavigate();
+ const { toastTrigger } = useToast();
+
+ useEffect(() => {
+ const fetchHashtags = async () => {
+ const fetchedHashtags = await getMyHashtag(userId);
+ setHashtags(fetchedHashtags);
+ };
+
+ fetchHashtags();
+ }, [userId]);
+
+ const onChannelNameCheck = async () => {
+ if (newChannelName === channelName) {
+ setChannelNameCheckMessage('ํ์ฌ ์ฑ๋ ์ด๋ฆ๊ณผ ๋์ผํฉ๋๋ค.');
+ setIsChannelNameChecked(true);
+ return;
+ }
+
+ await checkChannelNameDuplicate(
+ newChannelName,
+ checkChannelNameExists,
+ '์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์ฑ๋ ์ด๋ฆ์
๋๋ค.',
+ '์ฌ์ฉ ๊ฐ๋ฅํ ์ฑ๋ ์ด๋ฆ์
๋๋ค.'
+ );
+ };
+
+ const onChannelNameChange = (
+ e: React.ChangeEvent
+ ) => {
+ setNewChannelName(e.target.value);
+ setIsChannelNameChecked(false);
+ setChannelNameCheckMessage('');
+ };
+
+ const onImageChange = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ setSelectedImage(file);
+ const objectUrl = URL.createObjectURL(file);
+ setPreviewUrl(objectUrl);
+ }
+ };
+
+ const onProfileSave = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (newChannelName !== channelName && !isChannelNameChecked) {
+ toastTrigger('์ฑ๋ ์ด๋ฆ ์ค๋ณต ๊ฒ์ฌ๋ฅผ ์งํํด์ฃผ์ธ์.', 'fail');
+ return;
+ }
+
+ if (selectedImage && userId) {
+ await updateProfileImage(userId, { profileImageFile: selectedImage });
+ }
+
+ if (userId) {
+ await updateProfileTags(userId, hashtags);
+
+ if (newChannelName !== channelName) {
+ const userRef = doc(db, 'users', userId);
+ await setDoc(userRef, { channelName: newChannelName }, { merge: true });
+ }
+ }
+
+ const userDocRef = doc(db, 'users', userId);
+ const userSnapshot = await getDoc(userDocRef);
+
+ if (userSnapshot.exists()) {
+ const updatedData = userSnapshot.data();
+ useAuthStore.setState({
+ profileImage: updatedData.profileImg || previewUrl,
+ channelName: updatedData.channelName || newChannelName,
+ });
+ }
+
+ toastTrigger('ํ๋กํ ์์ ์ด ์๋ฃ๋์์ต๋๋ค.', 'success');
+ navigate(ROUTES.PROFILE(userId));
+ };
+
+ const validateChannelName = (value: string) => {
+ if (value.length < 2) {
+ return '์ฑ๋ ์ด๋ฆ์ 2์๋ฆฌ ์ด์์ด์ด์ผ ํฉ๋๋ค';
+ }
+ return '';
+ };
+
+ return (
+
+
+
+
+
{channelName}
+
+
+
+
+ );
+};
+
+const containerStyle = css`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+`;
+
+const profileContainerStyle = css`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ margin: 20px 0;
+ padding-bottom: 20px;
+ border-bottom: 1px solid ${colors.gray02};
+`;
+
+const profileStyle = css`
+ position: relative;
+ width: 120px;
+ height: 120px;
+ cursor: pointer;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: ${colors.black};
+ opacity: 0.3;
+ border-radius: 50%;
+ }
+
+ &:hover {
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: ${colors.black};
+ opacity: 0.5;
+ border-radius: 50%;
+ }
+ }
+`;
+
+const cameraIconStyle = css`
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+`;
+
+const profileNameStyle = css`
+ margin: 6px 0;
+ font-size: ${fontSize.xl};
+ color: ${colors.black};
+`;
+
+const duplicateStyle = css`
+ display: flex;
+ align-items: center;
+ position: relative;
+
+ button {
+ height: 36px;
+ position: absolute;
+ right: 0;
+ top: 18px;
+ }
+`;
+
+const formContainerStyle = css`
+ max-width: 390px;
+`;
diff --git a/src/router/ProtectedRoute.tsx b/src/router/ProtectedRoute.tsx
new file mode 100644
index 0000000..20d6793
--- /dev/null
+++ b/src/router/ProtectedRoute.tsx
@@ -0,0 +1,19 @@
+import React, { ReactNode } from 'react';
+import { Navigate } from 'react-router-dom';
+import ROUTES from '@/constants/route';
+import { useAuthStore } from '@/stores/useAuthStore';
+
+interface ProtectedRouteProps {
+ children: ReactNode;
+}
+
+const ProtectedRoute: React.FC = ({ children }) => {
+ const { user } = useAuthStore();
+
+ if (!user) {
+ return ;
+ }
+ return <>{children}>;
+};
+
+export default ProtectedRoute;
diff --git a/src/router/router.tsx b/src/router/router.tsx
new file mode 100644
index 0000000..107b8c5
--- /dev/null
+++ b/src/router/router.tsx
@@ -0,0 +1,138 @@
+// src/router.js
+import { createBrowserRouter } from 'react-router-dom';
+import { Layout } from '@/components/Layout';
+import ROUTES from '@/constants/route';
+import Detail from '@/pages/common/Detail';
+import Following from '@/pages/common/Following';
+import HashTag from '@/pages/common/HashTag';
+import Home from '@/pages/common/Home';
+import { NotFound } from '@/pages/common/NotFound';
+import { Popular } from '@/pages/common/Popular';
+import Search from '@/pages/common/Search';
+import { SignIn } from '@/pages/common/SignIn';
+import { SignUp } from '@/pages/common/SignUp';
+import { PlayList } from '@/pages/playlist/PlayList';
+import PlayListAdd from '@/pages/playlist/PlayListAdd';
+import PlayListEdit from '@/pages/playlist/PlayListEdit';
+import { PlayListHome } from '@/pages/playlist/PlayListHome';
+import { PlayListLikes } from '@/pages/playlist/PlayListLikes';
+import { PlayListSaved } from '@/pages/playlist/PlayListSaved';
+import { Profile } from '@/pages/profile/Profile';
+import ProfileFollower from '@/pages/profile/ProfileFollower';
+import ProfileFollowing from '@/pages/profile/ProfileFollowing';
+import { ProfileHome } from '@/pages/profile/ProfileHome';
+import { ProfileUpdate } from '@/pages/profile/ProfileUpdate';
+import ProtectedRoute from '@/router/ProtectedRoute';
+
+export const router = createBrowserRouter([
+ {
+ path: ROUTES.ROOT,
+ element: ,
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ {
+ path: ROUTES.PLAYLIST(),
+ element: (
+
+
+
+ ),
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ {
+ path: ROUTES.PLAYLIST_SAVED(),
+ element: ,
+ },
+ {
+ path: ROUTES.PLAYLIST_LIKES(),
+ element: ,
+ },
+ ],
+ },
+ {
+ path: ROUTES.PLAYLIST_ADD(),
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: ROUTES.PLAYLIST_MODIFY(),
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: ROUTES.PROFILE(),
+ element: (
+
+
+
+ ),
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ {
+ path: ROUTES.PROFILE_MODIFY(),
+ element: ,
+ },
+ {
+ path: ROUTES.PROFILE_FOLLOWING(),
+ element: ,
+ },
+ {
+ path: ROUTES.PROFILE_FOLLOWER(),
+ element: ,
+ },
+ ],
+ },
+ {
+ path: ROUTES.POPULAR,
+ element: ,
+ },
+ {
+ path: ROUTES.FOLLOWING,
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: ROUTES.SEARCH(),
+ element: ,
+ },
+ {
+ path: ROUTES.DETAIL(),
+ element: ,
+ },
+ ],
+ },
+ {
+ path: ROUTES.SIGN_IN,
+ element: ,
+ },
+ {
+ path: ROUTES.SIGN_UP,
+ element: ,
+ },
+ {
+ path: ROUTES.HASH_TAG,
+ element: ,
+ },
+ {
+ path: ROUTES.NOT_FOUND,
+ element: ,
+ },
+]);
diff --git a/src/stores/useAuthStore.ts b/src/stores/useAuthStore.ts
new file mode 100644
index 0000000..49fb30a
--- /dev/null
+++ b/src/stores/useAuthStore.ts
@@ -0,0 +1,180 @@
+import { User, onAuthStateChanged } from 'firebase/auth';
+import {
+ doc,
+ getDoc,
+ collection,
+ query,
+ where,
+ getDocs,
+ updateDoc,
+ arrayRemove,
+} from 'firebase/firestore';
+import { create, StateCreator } from 'zustand';
+import { persist, PersistOptions } from 'zustand/middleware';
+import defaultProfile from '@/assets/profile_default.png';
+import { auth, db } from '@/firebase/firbaseConfig';
+
+interface AuthState {
+ user: User | null;
+ userId: string;
+ profileImage: string;
+ channelName: string;
+ likedPlaylist: number[];
+ savedPlaylist: number[];
+ channelFollower: string[];
+ channelFollowing: string[];
+ tags: string[];
+ isFirstLogin: boolean;
+ setUser: (user: User | null) => void;
+ clearUser: () => void;
+ fetchUserData: (userId: string) => void;
+ getIsFirstLogin: () => boolean;
+ addLikedPlaylistItem: (playlistId: number) => void;
+ addSavedPlaylistItem: (playlistId: number) => void;
+ removeLikedPlaylistItem: (playlistId: number) => void;
+ removeSavedPlaylistItem: (playlistId: number) => void;
+ removeFollowing: (userId: string, removeChannel: string) => Promise;
+ removeFollower: (userId: string, removeChannel: string) => Promise;
+}
+
+type AuthPersist = (
+ config: StateCreator,
+ options: PersistOptions
+) => StateCreator;
+
+const INIT_VALUES = {
+ user: {
+ user: null,
+ userId: '',
+ profileImage: '',
+ channelName: '',
+ likedPlaylist: [],
+ savedPlaylist: [],
+ channelFollower: [],
+ channelFollowing: [],
+ tags: [],
+ },
+};
+
+export const useAuthStore = create(
+ (persist as AuthPersist)(
+ (set, get) => ({
+ user: null,
+ userId: '',
+ profileImage: '',
+ channelName: '',
+ likedPlaylist: [],
+ savedPlaylist: [],
+ channelFollower: [],
+ channelFollowing: [],
+ tags: [],
+ isFirstLogin: true,
+ setUser: (user) => set({ user }),
+ clearUser: () => set(INIT_VALUES.user),
+ fetchUserData: async (id) => {
+ const userDocRef = doc(db, 'users', id);
+ const docSnapshot = await getDoc(userDocRef);
+ if (docSnapshot.exists()) {
+ const data = docSnapshot.data();
+ set({
+ profileImage: data?.profileImg || defaultProfile,
+ channelName: data?.channelName || '',
+ likedPlaylist: data?.likedPlaylist || [],
+ savedPlaylist: data?.savedPlaylist || [],
+ channelFollower: data?.channelFollower || [],
+ channelFollowing: data?.channelFollowing || [],
+ tags: data?.tags || [],
+ isFirstLogin: data?.isFirstLogin,
+ });
+ }
+ },
+ getIsFirstLogin: () => get().isFirstLogin,
+ addLikedPlaylistItem: (playlistId) =>
+ set((state) => ({
+ likedPlaylist: [...state.likedPlaylist, playlistId],
+ })),
+ addSavedPlaylistItem: (playlistId) =>
+ set((state) => ({
+ savedPlaylist: [...state.savedPlaylist, playlistId],
+ })),
+ removeLikedPlaylistItem: (playlistId) =>
+ set((state) => ({
+ likedPlaylist: state.likedPlaylist.filter((i) => i !== playlistId),
+ })),
+ removeSavedPlaylistItem: (playlistId) =>
+ set((state) => ({
+ savedPlaylist: state.savedPlaylist.filter((i) => i !== playlistId),
+ })),
+
+ removeFollowing: async (userId: string, removeChannel: string) => {
+ const userDocRef = doc(db, 'users', userId);
+ await updateDoc(userDocRef, {
+ channelFollowing: arrayRemove(removeChannel),
+ });
+ set((state) => ({
+ channelFollowing: state.channelFollowing.filter(
+ (userId) => userId !== removeChannel
+ ),
+ }));
+ },
+
+ removeFollower: async (userId: string, removeChannel: string) => {
+ const userDocRef = doc(db, 'users', userId);
+ await updateDoc(userDocRef, {
+ channelFollower: arrayRemove(removeChannel),
+ });
+ set((state) => ({
+ channelFollower: state.channelFollower.filter(
+ (userId) => userId !== removeChannel
+ ),
+ }));
+ },
+ }),
+ {
+ name: 'auth-storage',
+ storage: {
+ getItem: (name) => {
+ const item = localStorage.getItem(name);
+ return item ? JSON.parse(item) : null;
+ },
+ setItem: (name, value) => {
+ localStorage.setItem(name, JSON.stringify(value));
+ },
+ removeItem: (name) => {
+ localStorage.removeItem(name);
+ },
+ },
+ }
+ )
+);
+
+export const updateFirstLogin = async (userId: string, tags: string[]) => {
+ const userDocRef = doc(db, 'users', userId);
+ await updateDoc(userDocRef, {
+ isFirstLogin: false,
+ tags,
+ });
+};
+
+onAuthStateChanged(auth, async (user) => {
+ const { setUser, fetchUserData } = useAuthStore.getState();
+ setUser(user);
+ if (user) {
+ const email = user.email || '';
+ const userId = email.split('@')[0];
+
+ const usersCollectionRef = collection(db, 'users');
+ const userQuery = query(usersCollectionRef, where('uid', '==', user.uid));
+ const querySnapshot = await getDocs(userQuery);
+
+ if (!querySnapshot.empty) {
+ useAuthStore.setState({ userId });
+
+ fetchUserData(userId);
+ } else {
+ console.error('User not found in Firestore');
+ }
+ } else {
+ useAuthStore.getState().clearUser();
+ }
+});
diff --git a/src/stores/useModalStore.ts b/src/stores/useModalStore.ts
new file mode 100644
index 0000000..e4c61e9
--- /dev/null
+++ b/src/stores/useModalStore.ts
@@ -0,0 +1,26 @@
+import { create } from 'zustand';
+
+type ModalType = 'confirm' | 'delete';
+
+interface ModalData {
+ type: ModalType;
+ title: string;
+ content: string;
+ onAction: () => void;
+}
+
+interface ModalStore {
+ isOpen: boolean;
+ modalData: ModalData | null;
+ openModal: (data: ModalData) => void;
+ closeModal: () => void;
+}
+
+const useModalStore = create((set) => ({
+ isOpen: false,
+ modalData: null,
+ openModal: (data: ModalData) => set({ isOpen: true, modalData: data }),
+ closeModal: () => set({ isOpen: false, modalData: null }),
+}));
+
+export default useModalStore;
diff --git a/src/stores/useToastStore.ts b/src/stores/useToastStore.ts
new file mode 100644
index 0000000..4dca209
--- /dev/null
+++ b/src/stores/useToastStore.ts
@@ -0,0 +1,23 @@
+import { create } from 'zustand';
+import { ToastStatusType } from '@/components/Toast';
+
+interface ToastState {
+ isToastOn: boolean;
+ status: ToastStatusType;
+ toastMsg: string;
+ toastOn: (msg: string, status?: ToastStatusType) => void;
+ toastOff: () => void;
+}
+
+export const useToastStore = create((set) => ({
+ isToastOn: false,
+ toastMsg: '',
+ status: 'success',
+
+ toastOn: (msg: string, status: ToastStatusType = 'success') => {
+ set({ isToastOn: true, toastMsg: msg, status });
+ },
+ toastOff: () => {
+ set({ isToastOn: false, toastMsg: '', status: 'success' });
+ },
+}));
diff --git a/src/stores/useVisibilityStore.ts b/src/stores/useVisibilityStore.ts
new file mode 100644
index 0000000..f6de8a5
--- /dev/null
+++ b/src/stores/useVisibilityStore.ts
@@ -0,0 +1,42 @@
+import { doc, updateDoc } from 'firebase/firestore';
+import { create } from 'zustand';
+import { db } from '@/firebase/firbaseConfig';
+
+interface PlaylistVisibility {
+ [playlistId: string]: boolean;
+}
+
+interface PlaylistVisibilityState {
+ visibilities: PlaylistVisibility;
+ toggleVisibility: (playlistId: string) => Promise;
+ setInitialVisibility: (playlistId: string, isPublic: boolean) => void;
+}
+
+export const useVisibilityStore = create(
+ (set, get) => ({
+ visibilities: {},
+ toggleVisibility: async (playlistId: string) => {
+ try {
+ const currentVisibility = get().visibilities[playlistId];
+ const playlistRef = doc(db, 'playlist', playlistId);
+ await updateDoc(playlistRef, {
+ isPublic: !currentVisibility,
+ });
+
+ set((state) => ({
+ visibilities: {
+ ...state.visibilities,
+ [playlistId]: !currentVisibility,
+ },
+ }));
+ } catch (err) {
+ console.error('์ฌ์ ๋ชฉ๋ก ๊ณต๊ฐ/๋น๊ณต๊ฐ ์
๋ฐ์ดํธ ์ค ์ค๋ฅ ๋ฐ์:', err);
+ }
+ },
+ setInitialVisibility: (playlistId: string, isPublic: boolean) => {
+ set((state) => ({
+ visibilities: { ...state.visibilities, [playlistId]: isPublic },
+ }));
+ },
+ })
+);
diff --git a/src/styles/GlobalStyles.tsx b/src/styles/GlobalStyles.tsx
new file mode 100644
index 0000000..aeebdbb
--- /dev/null
+++ b/src/styles/GlobalStyles.tsx
@@ -0,0 +1,171 @@
+import { Global, css } from '@emotion/react';
+import colors from '@/constants/colors';
+import { fontSize, fontWeight } from '@/constants/font';
+
+const GlobalStyles = () => (
+
+);
+
+export default GlobalStyles;
diff --git a/src/tests/signIn.spec.js b/src/tests/signIn.spec.js
new file mode 100644
index 0000000..3c19c0c
--- /dev/null
+++ b/src/tests/signIn.spec.js
@@ -0,0 +1,73 @@
+import { test, expect } from '@playwright/test';
+
+const TEST_USER_ID = 'test1234';
+const TEST_PASSWORD = 'test1234';
+
+test.describe('๋ก๊ทธ์ธ ํ์ด์ง', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/signin');
+ });
+
+ test('๋ก๊ทธ์ธ ํผ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋ ๋๋ง๋๋์ง ํ
์คํธ', async ({ page }) => {
+ await expect(page.getByAltText('์ํ๋ฆฌ ๋ก๊ณ ')).toBeVisible();
+ await expect(page.getByPlaceholder('์์ด๋')).toBeVisible();
+ await expect(page.getByPlaceholder('๋น๋ฐ๋ฒํธ')).toBeVisible();
+ await expect(
+ page.getByRole('button', { name: '๋ก๊ทธ์ธ', exact: true })
+ ).toBeVisible();
+ await expect(
+ page.getByRole('button', { name: /๊ตฌ๊ธ๋ก ๋ก๊ทธ์ธ/i })
+ ).toBeVisible();
+ await expect(page.getByText('๊ณ์ ์ด ์์ผ์ ๊ฐ์?')).toBeVisible();
+ await expect(page.getByText('ํ์๊ฐ์
ํ๊ธฐ')).toBeVisible();
+ });
+
+ test('ํ๋๊ฐ ๋น์ด์์ ๋ ๋ก๊ทธ์ธ ๋ฒํผ์ด ๋นํ์ฑํ๋๋์ง ํ
์คํธ', async ({
+ page,
+ }) => {
+ const loginButton = page.getByRole('button', {
+ name: '๋ก๊ทธ์ธ',
+ exact: true,
+ });
+ await expect(loginButton).toBeDisabled();
+ });
+
+ test('ํ๋๊ฐ ์ฑ์์ก์ ๋ ๋ก๊ทธ์ธ ๋ฒํผ์ด ํ์ฑํ๋๋์ง ํ
์คํธ', async ({
+ page,
+ }) => {
+ await page.getByPlaceholder('์์ด๋').fill(TEST_USER_ID);
+ await page.getByPlaceholder('๋น๋ฐ๋ฒํธ').fill(TEST_PASSWORD);
+ const loginButton = page.getByRole('button', {
+ name: '๋ก๊ทธ์ธ',
+ exact: true,
+ });
+ await expect(loginButton).toBeEnabled();
+ });
+
+ test('์๋ชป๋ ๋ก๊ทธ์ธ ์๋ ์ ์๋ฌ ๋ฉ์์ง๊ฐ ํ์๋๋์ง ํ
์คํธ', async ({
+ page,
+ }) => {
+ await page.getByPlaceholder('์์ด๋').fill('invaliduser');
+ await page.getByPlaceholder('๋น๋ฐ๋ฒํธ').fill('wrongpassword');
+ await page.getByRole('button', { name: '๋ก๊ทธ์ธ', exact: true }).click();
+ await expect(
+ page.getByText('์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํด์ฃผ์ธ์')
+ ).toBeVisible();
+ });
+
+ test('ํ์๊ฐ์
ํ๊ธฐ ๋งํฌ ํด๋ฆญ ์ ํ์๊ฐ์
ํ์ด์ง๋ก ์ด๋ํ๋์ง ํ
์คํธ', async ({
+ page,
+ }) => {
+ await page.getByText('ํ์๊ฐ์
ํ๊ธฐ').click();
+ await expect(page).toHaveURL(/.*\/signup/);
+ });
+
+ test('Google ๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญ์ด ์ ์์ ์ผ๋ก ์๋ํ๋์ง ํ
์คํธ', async ({
+ page,
+ }) => {
+ const googleLoginButton = page.getByRole('button', {
+ name: /๊ตฌ๊ธ๋ก ๋ก๊ทธ์ธ/i,
+ });
+ await googleLoginButton.click();
+ });
+});
diff --git a/src/types/api.ts b/src/types/api.ts
new file mode 100644
index 0000000..716a197
--- /dev/null
+++ b/src/types/api.ts
@@ -0,0 +1,13 @@
+export interface ApiResponse {
+ status: 'success' | 'fail';
+ result?: T;
+}
+
+export interface UserProps {
+ channelFollower: string[];
+ channelFollowing: string[];
+ channelName: string;
+ profileImg: string;
+ isMyChannel: boolean;
+ userId: string;
+}
diff --git a/src/types/header.ts b/src/types/header.ts
new file mode 100644
index 0000000..91cd67c
--- /dev/null
+++ b/src/types/header.ts
@@ -0,0 +1,6 @@
+export type HeaderType = 'main' | 'searchResult' | 'detail';
+
+export interface HeaderProps {
+ type: HeaderType;
+ headerTitle?: string;
+}
diff --git a/src/types/playlistType.ts b/src/types/playlistType.ts
new file mode 100644
index 0000000..ff57068
--- /dev/null
+++ b/src/types/playlistType.ts
@@ -0,0 +1,29 @@
+export interface PlayListDataProps {
+ playlistId?: string;
+ title: string;
+ description?: string;
+ isPublic: boolean;
+ likes: number;
+ links: string[];
+ regDate: string;
+ tags: string[];
+ thumbnail: string;
+ thumbnailFile?: File;
+ userId: string;
+ ownerChannelName: string;
+ commentCount?: number;
+}
+
+export interface ThumbnailProps {
+ file: File;
+ preview: string;
+}
+
+export interface CommentProps {
+ docId?: string;
+ content: string;
+ isEdited: boolean;
+ playlistId: number;
+ regDate: string;
+ userId: string;
+}
diff --git a/src/types/route.ts b/src/types/route.ts
new file mode 100644
index 0000000..91cd67c
--- /dev/null
+++ b/src/types/route.ts
@@ -0,0 +1,6 @@
+export type HeaderType = 'main' | 'searchResult' | 'detail';
+
+export interface HeaderProps {
+ type: HeaderType;
+ headerTitle?: string;
+}
diff --git a/src/types/video.ts b/src/types/video.ts
new file mode 100644
index 0000000..44a6377
--- /dev/null
+++ b/src/types/video.ts
@@ -0,0 +1,5 @@
+export interface VideoProps {
+ imgUrl: string;
+ title: string;
+ userName: string;
+}
diff --git a/src/utils/checkDuplicate.ts b/src/utils/checkDuplicate.ts
new file mode 100644
index 0000000..1f0d9ee
--- /dev/null
+++ b/src/utils/checkDuplicate.ts
@@ -0,0 +1,34 @@
+import {
+ collection,
+ query,
+ where,
+ getDocs,
+ doc,
+ getDoc,
+} from 'firebase/firestore';
+import { db } from '@/firebase/firbaseConfig';
+
+export const checkChannelNameExists = async (
+ channelName: string
+): Promise => {
+ const q = query(
+ collection(db, 'users'),
+ where('channelName', '==', channelName)
+ );
+ try {
+ const querySnapshot = await getDocs(q);
+ return !querySnapshot.empty;
+ } catch (error) {
+ throw new Error('์ฑ๋ ์ด๋ฆ ์ค๋ณต ์ฒดํฌ์ ์คํจํ์ต๋๋ค.');
+ }
+};
+
+export const checkIdExists = async (id: string): Promise => {
+ try {
+ const docRef = doc(db, 'users', id);
+ const docSnap = await getDoc(docRef);
+ return docSnap.exists();
+ } catch (error) {
+ throw new Error('์์ด๋ ์ค๋ณต ์ฒดํฌ์ ์คํจํ์ต๋๋ค.');
+ }
+};
diff --git a/src/utils/filterUtils.ts b/src/utils/filterUtils.ts
new file mode 100644
index 0000000..67ff5f1
--- /dev/null
+++ b/src/utils/filterUtils.ts
@@ -0,0 +1,42 @@
+import { HASHTAGS } from '@/constants/hashtag';
+import { PlayListDataProps } from '@/types/playlistType';
+
+// ๊ณต๊ฐ์ฌ๋ถ ํํฐ๋ง
+export const filterPlaylistByVisibility = (
+ playlist: PlayListDataProps[],
+ visibilityOption: number
+): PlayListDataProps[] => {
+ if (visibilityOption === 0) return playlist;
+ return playlist.filter((item) =>
+ visibilityOption === 1 ? item.isPublic : !item.isPublic
+ );
+};
+
+// ์ ๋ ฌ ํํฐ๋ง (์ข์์์, ๋๊ธ์, ์ต์ ์)
+export const sortPlaylistsByOption = (
+ playlist: PlayListDataProps[],
+ sortOption: number
+): PlayListDataProps[] =>
+ [...playlist].sort((a, b) => {
+ switch (sortOption) {
+ case 1:
+ return b.likes - a.likes;
+ case 2:
+ return (b.commentCount ?? 0) - (a.commentCount ?? 0);
+ default:
+ return new Date(b.regDate).getTime() - new Date(a.regDate).getTime();
+ }
+ });
+
+export const randomTags = (): string[] => {
+ const randomValues = new Set();
+ while (randomValues.size < 3) {
+ randomValues.add(Math.floor(Math.random() * HASHTAGS.length));
+ }
+ const randomTagIndex: number[] = Array.from(randomValues) as number[];
+
+ const defaultTags: string[] = [...randomTagIndex.values()].map((tagindex) =>
+ HASHTAGS[tagindex].label.replace('#', '')
+ );
+ return defaultTags;
+};
diff --git a/src/utils/headerUtils.ts b/src/utils/headerUtils.ts
new file mode 100644
index 0000000..54f01a8
--- /dev/null
+++ b/src/utils/headerUtils.ts
@@ -0,0 +1,35 @@
+import ROUTES from '@/constants/route';
+import { HeaderProps } from '@/types/header';
+
+const HEADER_FILTER = {
+ main: [],
+ detail: [
+ { url: ROUTES.PROFILE_MODIFY(), title: 'ํ๋กํ ์์ ' },
+ { url: ROUTES.PROFILE_FOLLOWER(), title: '๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก' },
+ { url: ROUTES.PROFILE_FOLLOWING(), title: '๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก' },
+ { url: ROUTES.PLAYLIST_ADD(), title: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ฑ' },
+ { url: ROUTES.PLAYLIST_MODIFY(), title: 'ํ๋ ์ด๋ฆฌ์คํธ ์์ ' },
+ { url: ROUTES.FOLLOWING_LIST(), title: '๋ชจ๋ ํ๋ก์ ๋ชฉ๋ก' },
+ ],
+ searchResult: [ROUTES.SEARCH()],
+};
+
+export const checkHeaderType = (path: string): HeaderProps => {
+ for (const route of HEADER_FILTER.searchResult) {
+ if (path.includes(route.split('/')[1])) return { type: 'searchResult' };
+ }
+
+ for (const route of HEADER_FILTER.detail) {
+ const r = route.url.split('/');
+ const p = path.split('/');
+ if (
+ r.length === p.length &&
+ path.includes(r[1]) &&
+ path.includes(r[r.length - 1])
+ ) {
+ return { type: 'detail', headerTitle: route.title };
+ }
+ }
+
+ return { type: 'main' };
+};
diff --git a/src/utils/profileUtils.ts b/src/utils/profileUtils.ts
new file mode 100644
index 0000000..d6b7fd6
--- /dev/null
+++ b/src/utils/profileUtils.ts
@@ -0,0 +1,2 @@
+export const getProfileImg = (userId: string) =>
+ `https://firebasestorage.googleapis.com/v0/b/tpj3test.appspot.com/o/profile%2F${userId}%2Fprofile.png?alt=media`;
diff --git a/src/utils/randomTags.ts b/src/utils/randomTags.ts
new file mode 100644
index 0000000..91a21f5
--- /dev/null
+++ b/src/utils/randomTags.ts
@@ -0,0 +1,5 @@
+function randomTags(arr: string[], count: number = 3): string[] {
+ const shuffled = arr.sort(() => 0.5 - Math.random());
+ return shuffled.slice(0, count);
+}
+export default randomTags;
diff --git a/src/utils/textUtils.ts b/src/utils/textUtils.ts
new file mode 100644
index 0000000..7f38a10
--- /dev/null
+++ b/src/utils/textUtils.ts
@@ -0,0 +1,39 @@
+const DEFAULT_MAX_LENGTH = 50;
+
+export const omittedText = (
+ value: string,
+ maxLength: number = DEFAULT_MAX_LENGTH
+): string => {
+ if (!!!value || value.trim().length < 1) return '';
+ if (value.length > maxLength)
+ return value.trim().substring(0, maxLength) + '...';
+
+ return value;
+};
+
+export const tagging = (value: string) => {
+ const trimValue = value.trim().split(' ').join('');
+ if (trimValue.length > 0) {
+ return trimValue.startsWith('#') ? trimValue : `#${trimValue}`;
+ }
+ return '';
+};
+
+export const convertUnitNumber = (value: number, fixed: number = 2): string => {
+ let result = value.toString();
+
+ const units = [
+ { text: '์ต', divNum: 100000000 },
+ { text: '๋ง', divNum: 10000 },
+ { text: '์ฒ', divNum: 1000 },
+ ];
+
+ units.some((unit) => {
+ if (value > unit.divNum) {
+ return (result =
+ parseFloat(Number(value / unit.divNum).toFixed(fixed)) + unit.text);
+ }
+ });
+
+ return result;
+};
diff --git a/src/utils/videoUtils.ts b/src/utils/videoUtils.ts
new file mode 100644
index 0000000..e906660
--- /dev/null
+++ b/src/utils/videoUtils.ts
@@ -0,0 +1,20 @@
+import { Regex } from '@/constants/validation';
+
+export interface VideoInfoProps {
+ link: string;
+ embedUrl?: string;
+ embedHtml?: string;
+ thumbnail: string;
+}
+
+export const getVideoId = (link: string): string => {
+ let videoId = '';
+ for (const key in Regex) {
+ const matchData = link.match(Regex[key]);
+ if (matchData) {
+ videoId = link.match(Regex[key])?.[1] as string;
+ return videoId;
+ }
+ }
+ return videoId;
+};
diff --git a/tsconfig.app.json b/tsconfig.app.json
index f0a2350..58b656a 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -1,5 +1,10 @@
{
"compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ },
+
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
@@ -13,6 +18,7 @@
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
+ "jsxImportSource": "@emotion/react",
/* Linting */
"strict": true,
diff --git a/vite.config.ts b/vite.config.ts
index 5a33944..0c07aa6 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,16 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
+import tsconfigPaths from 'vite-tsconfig-paths';
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()],
-})
+ plugins: [
+ react({
+ jsxImportSource: '@emotion/react',
+ babel: {
+ plugins: ['@emotion/babel-plugin'],
+ },
+ }),
+ tsconfigPaths(),
+ ],
+});