diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/3\354\243\274\354\260\250 \353\257\270\354\205\230/mission1/WEB_STUDY" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/3\354\243\274\354\260\250 \353\257\270\354\205\230/mission1/WEB_STUDY" new file mode 160000 index 00000000..8d92b9f9 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/3\354\243\274\354\260\250 \353\257\270\354\205\230/mission1/WEB_STUDY" @@ -0,0 +1 @@ +Subproject commit 8d92b9f9ed25efbf01de71589c639b873024cff1 diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/.gitignore" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/.gitignore" new file mode 100644 index 00000000..1cac5597 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/.gitignore" @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/index.html" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/index.html" new file mode 100644 index 00000000..440d0f63 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/index.html" @@ -0,0 +1,13 @@ + + + + + + + Movie App + + +
+ + + diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/package.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/package.json" new file mode 100644 index 00000000..b9a04171 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/package.json" @@ -0,0 +1,35 @@ +{ + "name": "mission1", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.3", + "@tanstack/react-query": "^5.74.8", + "@tanstack/react-query-devtools": "^5.74.8", + "axios": "^1.9.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.5.0", + "tailwindcss": "^4.1.3" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/App.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/App.tsx" new file mode 100644 index 00000000..c04f62db --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/App.tsx" @@ -0,0 +1,22 @@ +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import Home from "./pages/home"; +import Movies from "./pages/movies"; +import MovieDetail from "./pages/moviedetail"; +import Layout from "./components/Navbar"; + +function App() { + return ( + + + }> + } /> + } /> + } /> + } /> + + + + ); +} + +export default App; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/apis/movie.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/apis/movie.ts" new file mode 100644 index 00000000..6524d4f4 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/apis/movie.ts" @@ -0,0 +1,31 @@ +import axios from "axios"; + +const TMDB_TOKEN = import.meta.env.VITE_TMDB_TOKEN; +const BASE_URL = import.meta.env.VITE_TMDB_BASE_URL; + +export type MovieType = "popular" | "now_playing" | "top_rated" | "upcoming"; + +export const getMovies = async (type: MovieType, page: number = 1) => { + const response = await axios.get(`${BASE_URL}/movie/${type}`, { + headers: { + Authorization: `Bearer ${TMDB_TOKEN}`, + }, + params: { + language: "ko-KR", + page, + }, + }); + return response.data; +}; + +export const getMovieDetail = async (id: number) => { + const response = await axios.get(`${BASE_URL}/movie/${id}`, { + headers: { + Authorization: `Bearer ${TMDB_TOKEN}`, + }, + params: { + language: "ko-KR", + }, + }); + return response.data; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/assets/profile.png" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/assets/profile.png" new file mode 100644 index 00000000..cccf18f9 Binary files /dev/null and "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/assets/profile.png" differ diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Error.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Error.tsx" new file mode 100644 index 00000000..6fa8762f --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Error.tsx" @@ -0,0 +1,16 @@ +import React from "react"; + +interface ErrorProps { + message: string; +} + +const Error = ({ message }: ErrorProps) => { + return ( +
+

오류가 발생했습니다

+

{message}

+
+ ); +}; + +export default Error; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Layout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Layout.tsx" new file mode 100644 index 00000000..a44d5743 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Layout.tsx" @@ -0,0 +1,16 @@ +import { ReactElement } from "react"; +import { Outlet } from "react-router-dom"; +import Navbar from "./Navbar"; + +const Layout = (): ReactElement => { + return ( +
+ +
+ +
+
+ ); +}; + +export default Layout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loading.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loading.tsx" new file mode 100644 index 00000000..0b908ca7 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loading.tsx" @@ -0,0 +1,13 @@ +import { ReactElement } from "react"; +import React from "react"; + +const Loading = (): ReactElement => { + return ( +
+
+

로딩 중...

+
+ ); +}; + +export default Loading; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loadingspinner.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loadingspinner.tsx" new file mode 100644 index 00000000..9e85a830 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Loadingspinner.tsx" @@ -0,0 +1,12 @@ +import { ReactElement } from "react"; + +const LoadingSpinner = (): ReactElement => { + return ( +
+
+

로딩 중...

+
+ ); +}; + +export default LoadingSpinner; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieCard.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieCard.tsx" new file mode 100644 index 00000000..d7e209d7 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieCard.tsx" @@ -0,0 +1,39 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import profileImage from "../assets/Profile.png"; + +interface MovieCardProps { + id: number; + title: string; + poster_path: string; + vote_average: number; + release_date: string; + overview: string; +} + +const MovieCard = ({ id, title, poster_path, overview }: MovieCardProps) => { + return ( + +
+ {title} { + const target = e.target as HTMLImageElement; + target.src = profileImage; + }} + /> +
+
+

{title}

+

{overview}

+
+ + ); +}; + +export default MovieCard; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieGrid.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieGrid.tsx" new file mode 100644 index 00000000..4e3ea2e8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieGrid.tsx" @@ -0,0 +1,20 @@ +// components/MovieGrid.tsx +import React from "react"; +import MovieCard from "./MovieCard"; +import { Movie } from "../hooks/useCustomFetch"; + +interface MovieGridProps { + movies: Movie[]; +} + +const MovieGrid = ({ movies }: MovieGridProps) => { + return ( +
+ {movies.map((movie) => ( + + ))} +
+ ); +}; + +export default MovieGrid; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieList.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieList.tsx" new file mode 100644 index 00000000..64fde314 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/MovieList.tsx" @@ -0,0 +1,41 @@ +import { useCustomFetch, MovieType } from "../hooks/useCustomFetch"; +import MovieGrid from "./MovieGrid"; +import Loading from "./Loading"; +import Error from "./Error"; +import Pagination from "./renderPagination"; +import React from "react"; + +interface MovieListProps { + type: MovieType; +} + +const MovieList = ({ type }: MovieListProps) => { + const { data, loading, error, goToPage, currentPage } = useCustomFetch({ + type, + }); + + return ( +
+ {loading ? ( +
+ +
+ ) : error ? ( + + ) : ( + <> + {data && data.total_pages > 1 && ( + + )} + + + )} +
+ ); +}; + +export default MovieList; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Navbar.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Navbar.tsx" new file mode 100644 index 00000000..5d25bed8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/Navbar.tsx" @@ -0,0 +1,39 @@ +import { Link, useLocation, Outlet } from "react-router-dom"; +import React from "react"; + +const Layout = () => { + const location = useLocation(); + + const links = [ + { path: "/", label: "홈" }, + { path: "/movies/popular", label: "인기" }, + { path: "/movies/now_playing", label: "상영 중" }, + { path: "/movies/top_rated", label: "평점 높은" }, + { path: "/movies/upcoming", label: "개봉 예정" }, + ]; + + return ( +
+ +
+ +
+
+ ); +}; + +export default Layout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/renderPagination.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/renderPagination.tsx" new file mode 100644 index 00000000..b7d312fc --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/components/renderPagination.tsx" @@ -0,0 +1,33 @@ +import React from "react"; + +type PaginationProps = { + currentPage: number; + totalPages: number; + goToPage: (page: number) => void; +}; + +const Pagination = ({ currentPage, totalPages, goToPage }: PaginationProps) => ( +
+ + + {currentPage} / {totalPages} + + +
+); + +export default Pagination; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomFetch.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomFetch.ts" new file mode 100644 index 00000000..937c1608 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomFetch.ts" @@ -0,0 +1,64 @@ +import { useState, useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; +import { getMovies } from "../apis/movie"; + +export type MovieType = "popular" | "now_playing" | "top_rated" | "upcoming"; + +interface UseCustomFetchProps { + type: MovieType; +} + +export interface Movie { + id: number; + title: string; + poster_path: string; + release_date: string; + vote_average: number; + overview: string; +} + +interface MovieResponse { + results: Movie[]; + total_pages: number; + total_results: number; + page: number; +} + +export const useCustomFetch = ({ type }: UseCustomFetchProps) => { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [searchParams, setSearchParams] = useSearchParams(); + + const page = parseInt(searchParams.get("page") || "1", 10); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await getMovies(type, page); + setData(response); + setError(null); + } catch (err) { + setError( + err instanceof Error + ? err + : new Error("알 수 없는 오류가 발생했습니다.") + ); + setData(null); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [type, page]); + + const goToPage = (newPage: number) => { + const newSearchParams = new URLSearchParams(searchParams); + newSearchParams.set("page", newPage.toString()); + setSearchParams(newSearchParams); + }; + + return { data, loading, error, goToPage, currentPage: page }; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomMovies.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomMovies.ts" new file mode 100644 index 00000000..15ab741e --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useCustomMovies.ts" @@ -0,0 +1,70 @@ +import { useState, useEffect } from "react"; +import axios from "axios"; + +interface Movie { + id: number; + title: string; + overview: string; + poster_path: string; + vote_average: number; + release_date: string; +} + +interface UseLatestMoviesReturn { + movies: Movie[]; + totalPages: number; + loading: boolean; + error: Error | null; +} + +const BASE_URL = "https://api.themoviedb.org/3"; +const TOKEN = import.meta.env.VITE_TMDB_TOKEN; + +export const useLatestMovies = (page: number = 1): UseLatestMoviesReturn => { + const [movies, setMovies] = useState([]); + const [totalPages, setTotalPages] = useState(1); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchLatestMovies = async () => { + try { + setLoading(true); + setError(null); + + const headers = { + Authorization: `Bearer ${TOKEN}`, + accept: "application/json", + }; + + const response = await axios.get( + `${BASE_URL}/movie/now_playing?language=ko-KR&page=${page}`, + { headers } + ); + + if (!response.data) { + throw new Error("API 응답이 비어있습니다."); + } + + if (!response.data.results || !response.data.total_pages) { + throw new Error("영화 데이터 형식이 올바르지 않습니다."); + } + + setMovies(response.data.results); + setTotalPages(response.data.total_pages); + } catch (err) { + setError( + err instanceof Error + ? err + : new Error("영화 데이터를 불러오는데 실패했습니다.") + ); + } finally { + setLoading(false); + } + }; + + fetchLatestMovies(); + }, [page]); + + return { movies, totalPages, loading, error }; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useMovieData.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useMovieData.ts" new file mode 100644 index 00000000..51e8d064 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/hooks/useMovieData.ts" @@ -0,0 +1,88 @@ +import { useState, useEffect } from "react"; +import axios from "axios"; + +interface MovieData { + id: number; + title: string; + overview: string; + poster_path: string; + vote_average: number; + release_date: string; + runtime?: number; + tagline?: string; + genres?: { id: number; name: string }[]; +} + +interface MovieCredits { + cast: { + id: number; + name: string; + character: string; + profile_path: string | null; + }[]; + crew: { + id: number; + name: string; + job: string; + }[]; +} + +interface UseMovieDataReturn { + data: MovieData | null; + credits: MovieCredits | null; + loading: boolean; + error: Error | null; +} + +const BASE_URL = "https://api.themoviedb.org/3"; +const TOKEN = import.meta.env.VITE_TMDB_TOKEN; + +export const useMovieData = ( + movieId: string | undefined +): UseMovieDataReturn => { + const [data, setData] = useState(null); + const [credits, setCredits] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchMovieData = async () => { + if (!movieId) { + setLoading(false); + return; + } + + try { + setLoading(true); + setError(null); + + const headers = { + Authorization: `Bearer ${TOKEN}`, + accept: "application/json", + }; + + const [movieResponse, creditsResponse] = await Promise.all([ + axios.get(`${BASE_URL}/movie/${movieId}?language=ko-KR`, { headers }), + axios.get(`${BASE_URL}/movie/${movieId}/credits?language=ko-KR`, { + headers, + }), + ]); + + setData(movieResponse.data); + setCredits(creditsResponse.data); + } catch (err) { + setError( + err instanceof Error + ? err + : new Error("An error occurred while fetching movie data") + ); + } finally { + setLoading(false); + } + }; + + fetchMovieData(); + }, [movieId]); + + return { data, credits, loading, error }; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/index.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/index.css" new file mode 100644 index 00000000..c5dd9fc4 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/index.css" @@ -0,0 +1,435 @@ +@import "tailwindcss/preflight"; +@import "tailwindcss/utilities"; +@import "tailwindcss"; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; + width: 100%; + overflow-x: hidden; +} + +.layout { + min-height: 100vh; + display: flex; + flex-direction: column; + width: 100%; +} + +.navbar { + background-color: #1a1a1a; + padding: 1rem 0; + width: 100%; + position: sticky; + top: 0; + z-index: 20; +} + +.navbar-container { + width: 100%; + padding: 0; + display: flex; + gap: 2rem; + justify-content: flex-start; + margin-left: 100px; +} + +.nav-link { + color: #ffffff; + text-decoration: none; + padding: 0.5rem 1rem; + border-radius: 4px; + transition: background-color 0.2s; +} + +.nav-link:hover { + background-color: #333333; +} + +.nav-link.active { + background-color: #4a4a4a; +} + +main { + flex: 1; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.movie-grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 20px; + justify-items: center; +} + +/* 반응형 디자인 */ +@media (max-width: 1200px) { + .movie-grid { + grid-template-columns: repeat(4, 1fr); + } +} + +@media (max-width: 768px) { + .movie-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (max-width: 576px) { + .movie-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 320px) { + .movie-grid { + grid-template-columns: 1fr; + } +} + +.movie-card { + position: relative; + width: 100%; + padding-top: 150%; + cursor: pointer; + transition: transform 0.2s; + overflow: hidden; + border-radius: 8px; + text-decoration: none; + color: inherit; +} + +.movie-poster { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.movie-poster img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: filter 0.3s ease; +} + +.movie-card:hover img { + filter: blur(5px) brightness(0.3); +} + +.movie-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + justify-content: center; + opacity: 0; + transition: opacity 0.3s ease; + border-radius: 8px; +} + +.movie-card:hover .movie-overlay { + opacity: 1; +} + +.movie-title { + color: white; + font-size: 1rem; + font-weight: bold; + margin-bottom: 0.5rem; + text-align: center; +} + +.movie-overview { + color: white; + font-size: 0.75rem; + line-height: 1.5; + padding: 0 1rem; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + text-overflow: ellipsis; +} + +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin: 1rem 0; + padding: 1rem; + position: sticky; + top: 64px; + z-index: 10; + background-color: transparent; +} + +.pagination-button { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + background-color: #1a1a1a; + color: white; + cursor: pointer; + transition: background-color 0.2s; +} + +.pagination-button:hover:not(.disabled) { + background-color: #333333; +} + +.pagination-button.disabled { + background-color: #cccccc; + cursor: not-allowed; + opacity: 0.7; +} + +.pagination-info { + font-size: 1rem; + color: #333333; + min-width: 100px; + text-align: center; +} + +.loading-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: calc(100vh - 300px); + margin-top: 2rem; +} + +.loading-spinner { + width: 50px; + height: 50px; + border: 5px solid #f3f3f3; + border-top: 5px solid #3498db; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +.loading-text { + margin-top: 1rem; + color: #666; + font-size: 1.1rem; + text-align: center; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.error-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 50vh; + padding: 2rem; + text-align: center; +} + +.error-message { + color: #e74c3c; + font-size: 1.2rem; + margin-bottom: 1rem; +} + +.retry-button { + padding: 0.5rem 1rem; + background-color: #3498db; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + transition: background-color 0.2s; +} + +.retry-button:hover { + background-color: #2980b9; +} + +.movie-detail { + padding-top: 4rem; + padding-bottom: 3rem; + padding-left: 2rem; + padding-right: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.movie-content { + display: flex; + gap: 2.5rem; + margin-bottom: 3rem; + align-items: flex-start; +} + +.movie-detail-poster { + flex-shrink: 0; + width: 300px; + height: auto; +} + +.movie-detail-poster img { + width: 100%; + height: auto; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + object-fit: cover; +} + +.movie-info { + flex: 1; +} + +.movie-info h1 { + font-size: 2.5rem; + margin: 0 0 1rem; + color: #1a1a1a; + line-height: 1.2; +} + +.tagline { + font-size: 1.2rem; + color: #666; + margin-bottom: 1.5rem; + font-style: italic; +} + +.movie-meta { + display: flex; + gap: 1.5rem; + margin-bottom: 1.5rem; + color: #555; +} + +.movie-meta span { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.genres { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 1.5rem; +} + +.genre { + background: #e9ecef; + padding: 0.4rem 1rem; + border-radius: 20px; + font-size: 0.9rem; + color: #495057; +} + +.overview { + font-size: 1.1rem; + line-height: 1.6; + color: #444; + margin-bottom: 2rem; +} + +.credits-section { + margin-top: 2rem; +} + +.credits-section h2 { + font-size: 1.8rem; + margin: 0 0 1.5rem; + color: #1a1a1a; + border-bottom: 2px solid #e9ecef; + padding-bottom: 0.5rem; +} + +.credits-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.credit-item { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.credit-image { + width: 150px; + height: 225px; + border-radius: 8px; + object-fit: cover; + margin-bottom: 1rem; +} + +.credit-info { + width: 100%; +} + +.credit-info h3 { + font-size: 1rem; + margin-bottom: 0.5rem; +} + +.credit-info p { + font-size: 0.9rem; + color: #666; +} + +@media (max-width: 768px) { + .movie-content { + flex-direction: column; + align-items: center; + } + + .movie-detail-poster { + width: 100%; + max-width: 300px; + margin-bottom: 1rem; + } + + .movie-info { + width: 100%; + text-align: center; + } + + .movie-meta { + flex-direction: column; + align-items: center; + } + + .genres { + justify-content: center; + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/main.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/main.tsx" new file mode 100644 index 00000000..5d2898b1 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/main.tsx" @@ -0,0 +1,13 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; + +const rootElement = document.getElementById("root"); +if (!rootElement) throw new Error("Failed to find the root element"); + +ReactDOM.createRoot(rootElement).render( + + + +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/home.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/home.tsx" new file mode 100644 index 00000000..ad0d8e12 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/home.tsx" @@ -0,0 +1,151 @@ +import { useEffect, useState, useCallback } from "react"; +import { useSearchParams } from "react-router-dom"; +import axios from "axios"; +import MovieCard from "../components/MovieCard"; +import LoadingSpinner from "../components/Loadingspinner"; +import type { TMovie, TMovieResponse } from "../types/movie"; + +const Home = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const [movies, setMovies] = useState([]); + const [totalPages, setTotalPages] = useState(1); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const currentPage = Number(searchParams.get("page")) || 1; + + const fetchMovies = useCallback(async () => { + try { + setIsLoading(true); + setError(null); + + const response = await axios.get( + `https://api.themoviedb.org/3/movie/popular?language=en-US&page=${currentPage}`, + { + headers: { + Authorization: `Bearer ${import.meta.env.VITE_TMDB_TOKEN}`, + }, + } + ); + + if ( + !response.data || + !response.data.results || + !response.data.total_pages + ) { + throw new Error("영화 데이터 형식이 올바르지 않습니다."); + } + + setMovies(response.data.results as TMovie[]); + + const maxPages = Math.min(response.data.total_pages, 500); + setTotalPages(maxPages); + } catch (error) { + console.error("Error fetching movies:", error); + setError( + error instanceof Error + ? error.message + : "영화 데이터를 불러오는데 실패했습니다." + ); + } finally { + setIsLoading(false); + } + }, [currentPage]); + + useEffect(() => { + fetchMovies(); + }, [fetchMovies]); + + const handlePageChange = (newPage: number) => { + if (newPage < 1 || newPage > totalPages) return; + setSearchParams({ page: newPage.toString() }); + }; + + const handlePageJump = (e: React.FormEvent) => { + e.preventDefault(); + const pageInput = ( + e.currentTarget.elements.namedItem("page") as HTMLInputElement + ).value; + const pageNum = Number(pageInput); + if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= totalPages) { + handlePageChange(pageNum); + } + }; + + const renderPagination = () => ( +
+ + + {currentPage} / {totalPages} + + +
+ + +
+
+ ); + + if (isLoading) { + return ( +
+ {renderPagination()} + +
+ ); + } + + if (error) { + return ( +
+

{error}

+ +
+ ); + } + + return ( +
+ {renderPagination()} +
+ {movies.map((movie: TMovie) => ( + + ))} +
+
+ ); +}; + +export default Home; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/moviedetail.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/moviedetail.tsx" new file mode 100644 index 00000000..793b2ed0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/moviedetail.tsx" @@ -0,0 +1,57 @@ +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import axios from "axios"; +import type { MovieDetail as MovieDetailType, Credits } from "../types/movie"; +import Loading from "../components/Loading"; +import Error from "../components/Error"; + +const MovieDetail = () => { + const { id } = useParams(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchMovie = async () => { + try { + setLoading(true); + const response = await axios.get( + `https://api.themoviedb.org/3/movie/${id}?api_key=${ + import.meta.env.VITE_TMDB_TOKEN + }&language=ko-KR&append_to_response=credits` + ); + setData(response.data); + } catch (err: any) { + setError(err); + } finally { + setLoading(false); + } + }; + + fetchMovie(); + }, [id]); + + if (loading) return ; + if (error) return ; + if (!data) return null; + + return ( +
+

+ {data.title} +

+ {data.title} +

+ {data.overview} +

+
+ ); +}; + +export default MovieDetail; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/movies.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/movies.tsx" new file mode 100644 index 00000000..39d9ea4d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/pages/movies.tsx" @@ -0,0 +1,21 @@ +import { useParams } from "react-router-dom"; +import { MovieType } from "../hooks/useCustomFetch"; +import MovieList from "../components/MovieList"; + +const Movies = () => { + const { category } = useParams<{ category: string }>(); + + const getMovieType = (): MovieType => { + const categoryToTypeMap: Record = { + popular: "popular", + now_playing: "now_playing", + top_rated: "top_rated", + upcoming: "upcoming", + }; + return categoryToTypeMap[category || ""] || "popular"; + }; + + return ; +}; + +export default Movies; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/postcss.config.js" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/postcss.config.js" new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/postcss.config.js" @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/routes/routes.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/routes/routes.tsx" new file mode 100644 index 00000000..9df27c3f --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/routes/routes.tsx" @@ -0,0 +1,16 @@ +import { createBrowserRouter } from "react-router-dom"; +import Home from "../pages/home"; +import React from "react"; + +const router = createBrowserRouter([ + { + path: "/home", + element: , + errorElement:
404 Not Found
, + }, + { + path: "/", + element:
, + }, +]); +export default router; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tailwind.config.js" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tailwind.config.js" new file mode 100644 index 00000000..3a400ba0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tailwind.config.js" @@ -0,0 +1,7 @@ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tsconfig.app.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tsconfig.app.json" new file mode 100644 index 00000000..5645a1f8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/tsconfig.app.json" @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "node", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* Types */ + "types": ["vite/client", "react/jsx-runtime"] + }, + "include": ["src"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/types/movie.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/types/movie.ts" new file mode 100644 index 00000000..7b6d78ac --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/types/movie.ts" @@ -0,0 +1,61 @@ +export interface MovieDetail { + id: number; + title: string; + overview: string; + poster_path: string; + release_date: string; + vote_average: number; + genres: { id: number; name: string }[]; + runtime: number; + budget: number; + revenue: number; + tagline: string; + status: string; + production_companies: { + id: number; + name: string; + logo_path: string | null; + }[]; +} + +export interface Credits { + cast: { + id: number; + name: string; + character: string; + profile_path: string | null; + credit_id: string; + }[]; + crew: Crew[]; +} + +export interface Crew { + id: number; + name: string; + job: string; + profile_path: string | null; + credit_id: string; +} + +// types/movie.ts +export type TMovie = { + id: number; + title: string; + poster_path: string; + vote_average: number; + release_date: string; + overview: string; +}; + +// types/movie.ts +export type TMovieResponse = { + page: number; + results: { + id: number; + title: string; + poster_path: string; + vote_average: number; + }[]; + total_pages: number; + total_results: number; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/vite-env.d.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/vite-env.d.ts" new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/src/vite-env.d.ts" @@ -0,0 +1 @@ +/// diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.json" new file mode 100644 index 00000000..81ff59e3 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.json" @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "jsx": "react-jsx", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true + }, + "include": ["src", "vite-env.d.ts"], + "exclude": ["node_modules", "dist"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.node.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.node.json" new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/tsconfig.node.json" @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/vite.config.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/vite.config.ts" new file mode 100644 index 00000000..3e8f4fb8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\230/vite.config.ts" @@ -0,0 +1,5 @@ +import { defineConfig } from "vite"; +import tailwindcss from "@tailwindcss/vite"; +export default defineConfig({ + plugins: [tailwindcss()], +}); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/.env" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/.env" new file mode 100644 index 00000000..93fa24af --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/.env" @@ -0,0 +1 @@ +VITE_API_URL=http://localhost:8000 \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/index.html" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/index.html" new file mode 100644 index 00000000..e0d1c840 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/index.html" @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/package.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/package.json" new file mode 100644 index 00000000..510d6548 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/package.json" @@ -0,0 +1,36 @@ +{ + "name": "week4-mission2", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@tailwindcss/vite": "^4.1.6", + "axios": "^1.9.0", + "react-hook-form": "^7.56.3", + "react-router-dom": "^7.6.0", + "zod": "^3.24.4" + }, + "description": "", + "devDependencies": { + "@tailwindcss/postcss": "^4.1.7", + "@types/axios": "^0.9.36", + "@types/node": "^22.15.17", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.4", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.4.1", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.3", + "tailwindcss": "^4.1.7", + "vite": "^6.3.5" + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/pnpm-lock.yaml" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/pnpm-lock.yaml" new file mode 100644 index 00000000..0721ff56 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/pnpm-lock.yaml" @@ -0,0 +1,1002 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.6 + version: 4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)) + tailwindcss: + specifier: ^4.1.6 + version: 4.1.6 + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.1.6': + resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==} + + '@tailwindcss/oxide-android-arm64@4.1.6': + resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.6': + resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.6': + resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.6': + resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': + resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': + resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': + resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': + resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.6': + resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.6': + resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': + resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': + resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.6': + resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.6': + resolution: {integrity: sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tailwindcss@4.1.6: + resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@rollup/rollup-android-arm-eabi@4.40.2': + optional: true + + '@rollup/rollup-android-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-x64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.2': + optional: true + + '@tailwindcss/node@4.1.6': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.29.2 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.6 + + '@tailwindcss/oxide-android-arm64@4.1.6': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.6': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.6': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.6': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.6': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': + optional: true + + '@tailwindcss/oxide@4.1.6': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.6 + '@tailwindcss/oxide-darwin-arm64': 4.1.6 + '@tailwindcss/oxide-darwin-x64': 4.1.6 + '@tailwindcss/oxide-freebsd-x64': 4.1.6 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.6 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.6 + '@tailwindcss/oxide-linux-x64-musl': 4.1.6 + '@tailwindcss/oxide-wasm32-wasi': 4.1.6 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.6 + + '@tailwindcss/vite@4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@tailwindcss/node': 4.1.6 + '@tailwindcss/oxide': 4.1.6 + tailwindcss: 4.1.6 + vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) + + '@types/estree@1.0.7': {} + + chownr@3.0.0: {} + + detect-libc@2.0.4: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + jiti@2.4.2: {} + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + + nanoid@3.3.11: {} + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rollup@4.40.2: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 + fsevents: 2.3.3 + + source-map-js@1.2.1: {} + + tailwindcss@4.1.6: {} + + tapable@2.2.1: {} + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + esbuild: 0.25.4 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.2 + tinyglobby: 0.2.13 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + + yallist@5.0.0: {} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/postcss.config.cjs" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/postcss.config.cjs" new file mode 100644 index 00000000..f7f7a3be --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/postcss.config.cjs" @@ -0,0 +1,6 @@ +// 수정 ㄴㄴ +module.exports = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/public/google-icon.svg" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/public/google-icon.svg" new file mode 100644 index 00000000..c911c1a9 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/public/google-icon.svg" @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/App.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/App.css" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/App.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/App.tsx" new file mode 100644 index 00000000..382b9578 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/App.tsx" @@ -0,0 +1,53 @@ +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import LoginPage from "./pages/LoginPage"; +import HomeLayout from "./layouts/HomeLayout"; +import HomePage from "./pages/HomePage"; +import SignupPage from "./pages/SignupPage"; +import NotFoundPage from "./pages/NotFoundPage"; +import MyPage from "./pages/MyPage"; +import { AuthProvider } from "./context/AuthContext"; +import { RouteObject } from "react-router-dom"; +import ProtectedLayout from "./layouts/ProtectedLayout"; +import GoogleLoginRedirectPage from "./pages/GoogleLoginRedirectPage"; + +//바로 접근 가능 +const publicRoutes: RouteObject[] = [ + { + path: "/", + element: , + errorElement: , + children: [ + { index: true, element: }, + { path: "login", element: }, + { path: "signup", element: }, + { path: "v1/auth/google/callback", element: }, + ], + }, +]; + +//인증하기기 +const protectedRoutes: RouteObject[] = [ + { + path: "/my-page", + element: , + errorElement: , + children: [ + { + index: true, + element: , + }, + ], + }, +]; + +const router = createBrowserRouter([...publicRoutes, ...protectedRoutes]); + +function App() { + return ( + + + + ); +} + +export default App; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/auth.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/auth.ts" new file mode 100644 index 00000000..54dad6ca --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/auth.ts" @@ -0,0 +1,70 @@ +import { axiosInstance } from "./axios"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; +import { ResponseMyInfoDto } from "../types/auth"; +import { AxiosError } from "axios"; + +// 회원가입 +export async function postSignup(data: { + email: string; + password: string; + name: string; +}) { + try { + const response = await axiosInstance.post("/v1/auth/signup", data); // ← 슬래시 추가 + console.log(" signup 응답 확인:", response.data); // ← 확인용 로그 추가 + return response.data; + } catch (error) { + console.error("Signup Error:", error); + if (error instanceof AxiosError) { + console.error("응답 내용:", error.response?.data); + } + throw error; + } +} + +// 로그인 +export async function postSignin(data: { email: string; password: string }) { + try { + const response = await axiosInstance.post("/v1/auth/signin", data); + console.log(" signin 응답 확인:", response.data); // ← 확인용 로그 추가 + return response.data; + } catch (error) { + console.error("Signin Error:", error); + if (error instanceof AxiosError) { + console.error("응답 내용:", error.response?.data); + } + throw error; + } +} + +// 내 정보 가져오기 +export async function getMyInfo(): Promise { + const token = localStorage.getItem(LOCAL_STORAGE_KEY.accessToken); + try { + const response = await axiosInstance.get("/v1/users/me", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + console.error("getMyInfo Error:", error); + throw error; + } +} + +// 로그아웃 +export const postLogout = async () => { + const token = localStorage.getItem(LOCAL_STORAGE_KEY.accessToken); + try { + const response = await axiosInstance.post("/v1/auth/signout", null, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + console.error("Logout Error:", error); + throw error; + } +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/axios.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/axios.ts" new file mode 100644 index 00000000..ce675f38 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/apis/axios.ts" @@ -0,0 +1,108 @@ +import axios, { AxiosRequestConfig, InternalAxiosRequestConfig } from "axios"; +import { LOCAL_STORAGE_KEY } from "../constants/key.ts"; +import { useLocalStorage } from "../hooks/useLocalStorage"; + +interface CustomAxiosRequestConfig extends AxiosRequestConfig { + _retry?: boolean; +} + +let refreshPromise: Promise | null = null; + +export const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_URL, +}); + +// 요청 인터셉터 +axiosInstance.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const { getItem } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + const accessToken = getItem(); + + // accessToken이 존재하면 Authorization 헤더에 Bearer 토큰 형식으로 추가한다. + if (accessToken) { + config.headers = config.headers || {}; + config.headers["Authorization"] = `Bearer ${accessToken}`; + } + + return config; + }, + (error) => Promise.reject(error) +); + +// 응답 인터셉터 +axiosInstance.interceptors.response.use( + (response) => response, //정상 응답 그대로 반환환 + async (error) => { + const originalRequest = error.config as CustomAxiosRequestConfig; + + if ( + error.response && + error.response.status === 401 && + !originalRequest._retry + ) { + if (originalRequest.url === "/v1/auth/refresh") { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + removeAccessToken(); + removeRefreshToken(); + window.location.href = "/login"; + return Promise.reject(error); + } + + originalRequest._retry = true; + + if (!refreshPromise) { + refreshPromise = (async () => { + const { getItem: getRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + const refreshToken = getRefreshToken(); + + const { data } = await axiosInstance.post("/v1/auth/refresh", { + refresh: refreshToken, + }); + + const { setItem: setAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { setItem: setRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + setAccessToken(data.data.accessToken); + setRefreshToken(data.data.refreshToken); + + return data.data.accessToken; + })() + .catch(() => { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + removeAccessToken(); + removeRefreshToken(); + }) + .finally(() => { + refreshPromise = null; + }); + } + + return refreshPromise.then((newAccessToken) => { + if (originalRequest.headers) { + originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; + } + return axiosInstance(originalRequest); + }); + } + + return Promise.reject(error); + } +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/assets/react.svg" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/assets/react.svg" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/components/Navbar.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/components/Navbar.tsx" new file mode 100644 index 00000000..b3160ef9 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/components/Navbar.tsx" @@ -0,0 +1,57 @@ +import { Link } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; + +const Navbar = () => { + const { accessToken, logout } = useAuth(); + + return ( + + ); +}; + +export default Navbar; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/constants/key.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/constants/key.ts" new file mode 100644 index 00000000..4b6c9308 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/constants/key.ts" @@ -0,0 +1,5 @@ +export const LOCAL_STORAGE_KEY: { accessToken: string; refreshToken: string } = + { + accessToken: "accessToken", + refreshToken: "refreshToken", + }; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/context/AuthContext.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/context/AuthContext.tsx" new file mode 100644 index 00000000..9be3bb22 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/context/AuthContext.tsx" @@ -0,0 +1,113 @@ +import { useLocalStorage } from "../hooks/useLocalStorage"; +import { RequestSigninDto } from "../types/auth"; +import { + createContext, + PropsWithChildren, + useState, + useContext, + useEffect, +} from "react"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; +import { postSignin, postLogout } from "../apis/auth"; + +interface AuthContextType { + accessToken: string | null; + refreshToken: string | null; + login: (signinData: RequestSigninDto) => Promise; + logout: () => Promise; +} + +export const AuthContext: React.Context = + createContext({ + accessToken: null, + refreshToken: null, + login: async () => {}, + logout: async () => {}, + }); + +export const AuthProvider = ({ children }: PropsWithChildren) => { + const { + getItem: getAccessTokenFromStorage, + setItem: setAccessTokenInStorage, + removeItem: removeAccessTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + + const { + getItem: getRefreshTokenFromStorage, + setItem: setRefreshTokenInStorage, + removeItem: removeRefreshTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.refreshToken); + + const [accessToken, setAccessToken] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + + useEffect(() => { + const storedAccessToken = getAccessTokenFromStorage(); + const storedRefreshToken = getRefreshTokenFromStorage(); + + if (storedAccessToken && storedRefreshToken) { + setAccessToken(storedAccessToken); + setRefreshToken(storedRefreshToken); + } else { + setAccessToken(null); + setRefreshToken(null); + } + }, []); + + const login = async (signinData: RequestSigninDto) => { + try { + console.log("로그인 시도:", signinData); + const response = await postSignin(signinData); + console.log("로그인 응답:", response); + + if (response && response.data) { + const { accessToken, refreshToken } = response.data; + console.log("토큰 정보:", { accessToken, refreshToken }); + + setAccessToken(accessToken); + setRefreshToken(refreshToken); + setAccessTokenInStorage(accessToken); + setRefreshTokenInStorage(refreshToken); + + console.log("토큰 저장 완료"); + alert("로그인 성공"); + window.location.href = "/my-page"; + } else { + console.error("로그인 응답이 올바르지 않습니다:", response); + alert("로그인 실패: 응답이 올바르지 않습니다"); + } + } catch (error) { + console.error("로그인 오류:", error); + alert("로그인 실패"); + } + }; + + const logout = async () => { + try { + const token = getAccessTokenFromStorage(); + if (token) { + await postLogout(); + } + removeAccessTokenFromStorage(); + removeRefreshTokenFromStorage(); + setAccessToken(null); + setRefreshToken(null); + alert("로그아웃 성공"); + window.location.href = "/"; + } catch (error) { + console.error("로그아웃 오류", error); + alert("로그아웃 실패"); + } + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + return context; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useForms.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useForms.ts" new file mode 100644 index 00000000..6ddab44b --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useForms.ts" @@ -0,0 +1,53 @@ +import { useState, useEffect } from "react"; + +interface UseFormProps { + initialValues: T; + validate: (values: T) => Partial; + onSubmit: (values: T) => void; +} + +function useForm({ initialValues, validate, onSubmit }: UseFormProps) { + const [values, setValues] = useState(initialValues); + const [touched, setTouched] = useState>( + {} as Record + ); + const [errors, setErrors] = useState>({}); + + const handleChange = (name: keyof T, text: string) => { + setValues({ + ...values, + [name]: text, + }); + }; + + const handleBlur = (name: keyof T) => { + setTouched({ + ...touched, + [name]: true, + }); + }; + + const getInputProps = (name: keyof T) => { + const value = values[name]; + const onChange = ( + e: React.ChangeEvent + ) => handleChange(name, e.target.value); + const onBlur = () => handleBlur(name); + + return { value, onChange, onBlur }; + }; + + useEffect(() => { + const newErrors = validate(values); + setErrors(newErrors); + }, [validate, values]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + onSubmit(values); + }; + + return { errors, touched, getInputProps, handleSubmit, values }; +} + +export default useForm; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useLocalStorage.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useLocalStorage.ts" new file mode 100644 index 00000000..2bede60f --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/hooks/useLocalStorage.ts" @@ -0,0 +1,27 @@ +export const useLocalStorage = (key: string) => { + const setItem = (value: unknown) => { + try { + window.localStorage.setItem(key, JSON.stringify(value)); + } catch (error) { + console.error(error); + } + }; + + const getItem = () => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : null; + } catch (error) { + console.error(error); + } + }; + + const removeItem = () => { + try { + window.localStorage.removeItem(key); + } catch (error) { + console.error(error); + } + }; + return { setItem, getItem, removeItem }; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/index.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/index.css" new file mode 100644 index 00000000..f1d8c73c --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/index.css" @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/HomeLayout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/HomeLayout.tsx" new file mode 100644 index 00000000..a8297440 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/HomeLayout.tsx" @@ -0,0 +1,15 @@ +import { Outlet } from "react-router-dom"; +import Navbar from "../components/Navbar"; + +const HomeLayout = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default HomeLayout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/ProtectedLayout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/ProtectedLayout.tsx" new file mode 100644 index 00000000..40088fca --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/layouts/ProtectedLayout.tsx" @@ -0,0 +1,13 @@ +import { useAuth } from "../context/AuthContext"; +import { Navigate, Outlet } from "react-router-dom"; + +const ProtectedLayout = () => { + const { accessToken } = useAuth(); + + if (!accessToken) { + return ; + } + return ; +}; + +export default ProtectedLayout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/main.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/main.tsx" new file mode 100644 index 00000000..0ebf5a7c --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/main.tsx" @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; +import "./index.css"; + +createRoot(document.getElementById("root")!).render( + + + +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/GoogleLoginRedirectPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/GoogleLoginRedirectPage.tsx" new file mode 100644 index 00000000..330a7f6c --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/GoogleLoginRedirectPage.tsx" @@ -0,0 +1,26 @@ +import { useEffect } from "react"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; + +function GoogleLoginRedirectPage() { + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + + const accessToken = urlParams.get(LOCAL_STORAGE_KEY.accessToken); + const refreshToken = urlParams.get(LOCAL_STORAGE_KEY.refreshToken); + + if (accessToken) { + // 로컬 스토리지에 저장 + localStorage.setItem(LOCAL_STORAGE_KEY.accessToken, accessToken); + } + + if (refreshToken) { + localStorage.setItem(LOCAL_STORAGE_KEY.refreshToken, refreshToken); + } + + window.location.href = "/my-page"; + }, []); + + return
구글 로그인 리다이렉트 화면
; +} + +export default GoogleLoginRedirectPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/HomePage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/HomePage.tsx" new file mode 100644 index 00000000..d5a244d2 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/HomePage.tsx" @@ -0,0 +1,10 @@ +function HomePage() { + return ( +
+

홈페이지

+

환영합니다!

+
+ ); +} + +export default HomePage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/LoginPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/LoginPage.tsx" new file mode 100644 index 00000000..ea7f8c74 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/LoginPage.tsx" @@ -0,0 +1,107 @@ +import useForm from "../hooks/useForms"; +import { validateSignin, UserSigninInformation } from "../utils/validate"; +import { AxiosError } from "axios"; +import { useAuth } from "../context/AuthContext"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { useEffect } from "react"; + +function LoginPage() { + const { login, accessToken } = useAuth(); + const navigate = useNavigate(); + + // useEffect(() => { + // if (accessToken) { + // navigate("/my-page"); + // } + // }, [accessToken, navigate]); + + const { errors, touched, getInputProps, handleSubmit } = + useForm({ + initialValues: { + email: "", + password: "", + }, + validate: validateSignin, + onSubmit: async (values) => { + console.log("로그인 성공:", values); + try { + await login(values); + } catch (error) { + if (error instanceof AxiosError) { + const errorMessage = + error.response?.data?.message || "로그인에 실패했습니다."; + alert(errorMessage); + } else { + alert("알 수 없는 오류가 발생했습니다."); + } + } + }, + }); + + const handleGoogleLogin = () => { + window.location.href = + import.meta.env.VITE_GOOGLE_LOGIN_URL + "v1/auth/google/login"; + }; + + return ( +
+
+
+ + < + +

로그인

+
+ +
+
+
+
+
+ OR +
+
+
+
+ + {errors?.email && touched?.email && ( +
{errors.email}
+ )} +
+
+ + {errors?.password && touched?.password && ( +
{errors.password}
+ )} +
+ +
+
+
+ ); +} + +export default LoginPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/MyPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/MyPage.tsx" new file mode 100644 index 00000000..096b03d7 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/MyPage.tsx" @@ -0,0 +1,98 @@ +import { useEffect, useState } from "react"; +import { getMyInfo } from "../apis/auth"; +import { ResponseMyInfoDto } from "../types/auth"; +import { useNavigate } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; + +const MyPage = () => { + const navigate = useNavigate(); + const { logout } = useAuth(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const getData = async () => { + try { + setLoading(true); + const response = await getMyInfo(); + console.log("MyInfo Response:", response); + setData(response); + setError(null); + } catch (error) { + console.error("Failed to fetch user info:", error); + setError("사용자 정보를 가져오는데 실패했습니다."); + } finally { + setLoading(false); + } + }; + getData(); + }, []); + + const handleLogout = async () => { + await logout(); + navigate("/"); + }; + + if (loading) { + return ( +
+

로딩 중...

+
+ ); + } + + if (error) { + return ( +
+

{error}

+ +
+ ); + } + + if (!data || !data.data || !data.data.name) { + return ( +
+

사용자 정보가 없습니다.

+ +
+ ); + } + + return ( +
+
+

+ {data.data.name}님 환영합니다. +

+ {data.data.avatar && ( + 프로필 이미지 + )} +

{data.data.email}

+ +
+
+ ); +}; + +export default MyPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/NotFoundPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/NotFoundPage.tsx" new file mode 100644 index 00000000..91f72221 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/NotFoundPage.tsx" @@ -0,0 +1,10 @@ +function NotFoundPage() { + return ( +
+

404

+

페이지를 찾을 수 없습니다.

+
+ ); +} + +export default NotFoundPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/SignupPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/SignupPage.tsx" new file mode 100644 index 00000000..810f2fb1 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/pages/SignupPage.tsx" @@ -0,0 +1,166 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Link } from "react-router-dom"; +import { postSignup } from "../apis/auth"; //여기에 내 api +import { AxiosError } from "axios"; +import { SignupForm, signupSchema } from "../utils/schema"; + +// +function SignupPage() { + const [showPassword, setShowPassword] = useState(false); + const [showPasswordConfirm, setShowPasswordConfirm] = useState(false); + const [isComplete, setIsComplete] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(signupSchema), + mode: "onChange", + }); + + const onSubmit = async (data: SignupForm) => { + console.log("회원가입 성공:", data); + try { + const response = await postSignup({ + email: data.email, + password: data.password, + name: data.nickname, + }); + + if (response) { + setIsComplete(true); + } else alert("회원가입에 실패했습니다."); + } catch (error) { + if (error instanceof AxiosError) { + alert(error.response?.data?.message || "회원가입에 실패했습니다."); + } else alert("알 수 없는 오류가 발생했습니다."); + } + }; + + return ( +
+
+
+ + < + +

회원가입

+
+ + {!isComplete ? ( + <> + +
+
+
+
+
+ OR +
+
+ +
+
+ + {errors.email && ( +
+ {errors.email.message} +
+ )} +
+ +
+ + + {errors.password && ( +
+ {errors.password.message} +
+ )} +
+ +
+ + + {errors.passwordConfirm && ( +
+ {errors.passwordConfirm.message} +
+ )} +
+ +
+ + {errors.nickname && ( +
+ {errors.nickname.message} +
+ )} +
+ + +
+ + ) : ( +
+
+ +
+

회원가입 완료!

+ + 로그인하기 + +
+ )} +
+
+ ); +} + +export default SignupPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/auth.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/auth.ts" new file mode 100644 index 00000000..f233a2e7 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/auth.ts" @@ -0,0 +1,41 @@ +import { CommonResponse } from "./common"; + +export type RequestSignupDto = { + name: string; + email: string; + bio?: string; + avatar?: string; + password: string; +}; + +export type ResponseSignupDto = CommonResponse<{ + id: number; + name: string; + email: string; + bio: string | null; + avatar: string | null; + createAt: string; + updateAt: string; +}>; + +export type RequestSigninDto = { + email: string; + password: string; +}; + +export type ResponseSigninDto = CommonResponse<{ + id: number; + name: string; + accessToken: string; + refreshToken: string; +}>; + +export type ResponseMyInfoDto = CommonResponse<{ + id: number; + name: string; + email: string; + bio: string | null; + avatar: string | null; + createAt: string; + updateAt: string; +}>; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/common.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/common.ts" new file mode 100644 index 00000000..0a1b1c2d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/types/common.ts" @@ -0,0 +1,6 @@ +export type CommonResponse = { + status: boolean; + statusCode: number; + message: string; + data: T; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/schema.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/schema.ts" new file mode 100644 index 00000000..0fd3dfa0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/schema.ts" @@ -0,0 +1,21 @@ +import { z } from "zod"; + +export const signupSchema = z + .object({ + email: z.string().email("이메일 형식이 올바르지 않습니다."), + password: z + .string() + .min(8, "비밀번호는 8자 이상이어야 합니다.") + .max(20, "비밀번호는 20자 이하여야 합니다."), + passwordConfirm: z.string(), + nickname: z + .string() + .min(2, "닉네임은 2자 이상이어야 합니다.") + .max(10, "닉네임은 10자 이하여야 합니다."), + }) + .refine((data) => data.password === data.passwordConfirm, { + message: "비밀번호가 일치하지 않습니다.", + path: ["passwordConfirm"], + }); + +export type SignupForm = z.infer; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/validate.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/validate.ts" new file mode 100644 index 00000000..bfee1409 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/utils/validate.ts" @@ -0,0 +1,65 @@ +export type UserSigninInformation = { + email: string; + password: string; +}; + +export type SignupForm = { + email: string; + password: string; + passwordConfirm: string; + nickname: string; +}; + +export function validateSignin( + values: UserSigninInformation +): Partial { + const errors: Partial = {}; + + if (!values.email) { + errors.email = "이메일을 입력해주세요."; + } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { + errors.email = "올바른 이메일 형식이 아닙니다."; + } + + if (!values.password) { + errors.password = "비밀번호를 입력해주세요"; + } else if (values.password.length < 8 || values.password.length > 20) { + errors.password = "비밀번호는 8자 이상 20자 이하로 입력해주세요"; + } + + return errors; +} + +export function validateSignupPassword( + values: SignupForm +): Partial { + const errors: Partial = {}; + + if (!values.password) { + errors.password = "비밀번호를 입력해주세요"; + } else if (values.password.length < 8 || values.password.length > 20) { + errors.password = "비밀번호는 8자 이상 20자 이하로 입력해주세요"; + } + + if (!values.passwordConfirm) { + errors.passwordConfirm = "비밀번호를 다시 입력해주세요"; + } else if (values.password !== values.passwordConfirm) { + errors.passwordConfirm = "비밀번호가 일치하지 않습니다"; + } + + return errors; +} + +export function validateSignupNickname( + values: Pick +): Partial> { + const errors: Partial> = {}; + + if (!values.nickname) { + errors.nickname = "닉네임을 입력해주세요"; + } else if (values.nickname.length < 2 || values.nickname.length > 10) { + errors.nickname = "닉네임은 2자 이상 10자 이하로 입력해주세요"; + } + + return errors; +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/vite-env.d.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/vite-env.d.ts" new file mode 100644 index 00000000..1a593c36 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/src/vite-env.d.ts" @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_API_URL: string; // 로긘 요청할 백엔드 API 주소 +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tailwind.config.js" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tailwind.config.js" new file mode 100644 index 00000000..614c86b4 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tailwind.config.js" @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.app.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.app.json" new file mode 100644 index 00000000..5645a1f8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.app.json" @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "node", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* Types */ + "types": ["vite/client", "react/jsx-runtime"] + }, + "include": ["src"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.json" new file mode 100644 index 00000000..f2574791 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.json" @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "types": ["vite/client"] + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.node.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.node.json" new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/tsconfig.node.json" @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/vite.config.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/vite.config.ts" new file mode 100644 index 00000000..1d8fa731 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/4\354\243\274\354\260\250_\353\257\270\354\205\2302/vite.config.ts" @@ -0,0 +1,7 @@ +// vite.config.ts +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], +}); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/.gitignore" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/.gitignore" new file mode 100644 index 00000000..1cac5597 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/.gitignore" @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/README.md" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/README.md" new file mode 100644 index 00000000..40ede56e --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/README.md" @@ -0,0 +1,54 @@ +# React + TypeScript + Vite + +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 + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/eslint.config.js" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/eslint.config.js" new file mode 100644 index 00000000..092408a9 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/eslint.config.js" @@ -0,0 +1,28 @@ +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/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/index.html" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/index.html" new file mode 100644 index 00000000..478cafed --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/index.html" @@ -0,0 +1,12 @@ + + + + + + + Vite + React + TS + +
+ + + diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/package.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/package.json" new file mode 100644 index 00000000..834fc6fa --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/package.json" @@ -0,0 +1,35 @@ +{ + "name": "mission1", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.3", + "@tanstack/react-query": "^5.74.8", + "@tanstack/react-query-devtools": "^5.74.8", + "axios": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.5.0", + "tailwindcss": "^4.1.3" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/pnpm-lock.yaml" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/pnpm-lock.yaml" new file mode 100644 index 00000000..26fa3467 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/pnpm-lock.yaml" @@ -0,0 +1,2625 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.3 + version: 4.1.4(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2)) + '@tanstack/react-query': + specifier: ^5.74.8 + version: 5.74.9(react@19.1.0) + '@tanstack/react-query-devtools': + specifier: ^5.74.8 + version: 5.74.9(@tanstack/react-query@5.74.9(react@19.1.0))(react@19.1.0) + axios: + specifier: ^1.9.0 + version: 1.9.0 + react: + specifier: ^19.0.0 + version: 19.1.0 + react-dom: + specifier: ^19.0.0 + version: 19.1.0(react@19.1.0) + react-router-dom: + specifier: ^7.5.0 + version: 7.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tailwindcss: + specifier: ^4.1.3 + version: 4.1.4 + devDependencies: + '@eslint/js': + specifier: ^9.21.0 + version: 9.25.1 + '@types/react': + specifier: ^19.0.10 + version: 19.1.2 + '@types/react-dom': + specifier: ^19.0.4 + version: 19.1.2(@types/react@19.1.2) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.4.1(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2)) + eslint: + specifier: ^9.21.0 + version: 9.25.1(jiti@2.4.2) + eslint-plugin-react-hooks: + specifier: ^5.1.0 + version: 5.2.0(eslint@9.25.1(jiti@2.4.2)) + eslint-plugin-react-refresh: + specifier: ^0.4.19 + version: 0.4.20(eslint@9.25.1(jiti@2.4.2)) + globals: + specifier: ^15.15.0 + version: 15.15.0 + typescript: + specifier: ~5.7.2 + version: 5.7.3 + typescript-eslint: + specifier: ^8.24.1 + version: 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + vite: + specifier: ^6.2.0 + version: 6.3.3(jiti@2.4.2)(lightningcss@1.29.2) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.8': + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.10': + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.27.0': + resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.0': + resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.0': + resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.0': + resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.27.0': + resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.27.0': + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.0': + resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.25.3': + resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.3': + resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.3': + resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.3': + resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.3': + resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.3': + resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.3': + resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.3': + resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.3': + resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.3': + resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.3': + resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.3': + resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.3': + resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.3': + resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.3': + resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.3': + resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.3': + resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.3': + resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.3': + resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.3': + resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.3': + resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.3': + resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.3': + resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.3': + resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.3': + resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.6.1': + resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.1': + resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.25.1': + resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@rollup/rollup-android-arm-eabi@4.40.1': + resolution: {integrity: sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.1': + resolution: {integrity: sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.1': + resolution: {integrity: sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.1': + resolution: {integrity: sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.1': + resolution: {integrity: sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.1': + resolution: {integrity: sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': + resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.1': + resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.1': + resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.1': + resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': + resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': + resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.1': + resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.1': + resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.1': + resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.1': + resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.1': + resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.1': + resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.1': + resolution: {integrity: sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.1': + resolution: {integrity: sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.1.4': + resolution: {integrity: sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==} + + '@tailwindcss/oxide-android-arm64@4.1.4': + resolution: {integrity: sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.4': + resolution: {integrity: sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.4': + resolution: {integrity: sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.4': + resolution: {integrity: sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4': + resolution: {integrity: sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.4': + resolution: {integrity: sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.4': + resolution: {integrity: sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.4': + resolution: {integrity: sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.4': + resolution: {integrity: sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.4': + resolution: {integrity: sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.4': + resolution: {integrity: sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.4': + resolution: {integrity: sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.4': + resolution: {integrity: sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.4': + resolution: {integrity: sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@tanstack/query-core@5.74.9': + resolution: {integrity: sha512-qmjXpWyigDw4SfqdSBy24FzRvpBPXlaSbl92N77lcrL+yvVQLQkf0T6bQNbTxl9IEB/SvVFhhVZoIlQvFnNuuw==} + + '@tanstack/query-devtools@5.74.7': + resolution: {integrity: sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw==} + + '@tanstack/react-query-devtools@5.74.9': + resolution: {integrity: sha512-6dMfeK/5OvC9E88/ziwiv1Pggqkgjker8V+pLJFrjh7O7E7S6yXJRNNr/KjA/c+z6d/i7HpDk8FF+oSr7mhYLg==} + peerDependencies: + '@tanstack/react-query': ^5.74.9 + react: ^18 || ^19 + + '@tanstack/react-query@5.74.9': + resolution: {integrity: sha512-F8xCXDQRDgsPzLzX9+d6ycNoITAIU2bycc1idZd06bt/GjN1quEJDjHvEDWZGoVn0A/ZmntVrYv6TE0kR7c7LA==} + peerDependencies: + react: ^18 || ^19 + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/react-dom@19.1.2': + resolution: {integrity: sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.2': + resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + + '@typescript-eslint/eslint-plugin@8.31.1': + resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.31.1': + resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.31.1': + resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.31.1': + resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.31.1': + resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.31.1': + resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.31.1': + resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.31.1': + resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@4.4.1': + resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001715: + resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.144: + resolution: {integrity: sha512-eJIaMRKeAzxfBSxtjYnoIAw/tdD6VIH6tHBZepZnAbE3Gyqqs5mGN87DvcldPUbVkIljTK8pY0CMcUljP64lfQ==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.3: + resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.20: + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.25.1: + resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.5.3: + resolution: {integrity: sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.5.3: + resolution: {integrity: sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.40.1: + resolution: {integrity: sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tailwindcss@4.1.4: + resolution: {integrity: sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + turbo-stream@2.4.0: + resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.31.1: + resolution: {integrity: sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite@6.3.3: + resolution: {integrity: sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.8': {} + + '@babel/core@7.26.10': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.27.0 + '@babel/helper-compilation-targets': 7.27.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helpers': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.27.0': + dependencies: + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.0': + dependencies: + '@babel/compat-data': 7.26.8 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.27.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.26.5': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.27.0': + dependencies: + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 + + '@babel/parser@7.27.0': + dependencies: + '@babel/types': 7.27.0 + + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/template@7.27.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + + '@babel/traverse@7.27.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.27.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@esbuild/aix-ppc64@0.25.3': + optional: true + + '@esbuild/android-arm64@0.25.3': + optional: true + + '@esbuild/android-arm@0.25.3': + optional: true + + '@esbuild/android-x64@0.25.3': + optional: true + + '@esbuild/darwin-arm64@0.25.3': + optional: true + + '@esbuild/darwin-x64@0.25.3': + optional: true + + '@esbuild/freebsd-arm64@0.25.3': + optional: true + + '@esbuild/freebsd-x64@0.25.3': + optional: true + + '@esbuild/linux-arm64@0.25.3': + optional: true + + '@esbuild/linux-arm@0.25.3': + optional: true + + '@esbuild/linux-ia32@0.25.3': + optional: true + + '@esbuild/linux-loong64@0.25.3': + optional: true + + '@esbuild/linux-mips64el@0.25.3': + optional: true + + '@esbuild/linux-ppc64@0.25.3': + optional: true + + '@esbuild/linux-riscv64@0.25.3': + optional: true + + '@esbuild/linux-s390x@0.25.3': + optional: true + + '@esbuild/linux-x64@0.25.3': + optional: true + + '@esbuild/netbsd-arm64@0.25.3': + optional: true + + '@esbuild/netbsd-x64@0.25.3': + optional: true + + '@esbuild/openbsd-arm64@0.25.3': + optional: true + + '@esbuild/openbsd-x64@0.25.3': + optional: true + + '@esbuild/sunos-x64@0.25.3': + optional: true + + '@esbuild/win32-arm64@0.25.3': + optional: true + + '@esbuild/win32-ia32@0.25.3': + optional: true + + '@esbuild/win32-x64@0.25.3': + optional: true + + '@eslint-community/eslint-utils@4.6.1(eslint@9.25.1(jiti@2.4.2))': + dependencies: + eslint: 9.25.1(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.1': {} + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.25.1': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.2': {} + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@rollup/rollup-android-arm-eabi@4.40.1': + optional: true + + '@rollup/rollup-android-arm64@4.40.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.1': + optional: true + + '@rollup/rollup-darwin-x64@4.40.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.1': + optional: true + + '@tailwindcss/node@4.1.4': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.29.2 + tailwindcss: 4.1.4 + + '@tailwindcss/oxide-android-arm64@4.1.4': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.4': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.4': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.4': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.4': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.4': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.4': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.4': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.4': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.4': + optional: true + + '@tailwindcss/oxide@4.1.4': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.4 + '@tailwindcss/oxide-darwin-arm64': 4.1.4 + '@tailwindcss/oxide-darwin-x64': 4.1.4 + '@tailwindcss/oxide-freebsd-x64': 4.1.4 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.4 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.4 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.4 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.4 + '@tailwindcss/oxide-linux-x64-musl': 4.1.4 + '@tailwindcss/oxide-wasm32-wasi': 4.1.4 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.4 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.4 + + '@tailwindcss/vite@4.1.4(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@tailwindcss/node': 4.1.4 + '@tailwindcss/oxide': 4.1.4 + tailwindcss: 4.1.4 + vite: 6.3.3(jiti@2.4.2)(lightningcss@1.29.2) + + '@tanstack/query-core@5.74.9': {} + + '@tanstack/query-devtools@5.74.7': {} + + '@tanstack/react-query-devtools@5.74.9(@tanstack/react-query@5.74.9(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/query-devtools': 5.74.7 + '@tanstack/react-query': 5.74.9(react@19.1.0) + react: 19.1.0 + + '@tanstack/react-query@5.74.9(react@19.1.0)': + dependencies: + '@tanstack/query-core': 5.74.9 + react: 19.1.0 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.7 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.27.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + + '@types/babel__traverse@7.20.7': + dependencies: + '@babel/types': 7.27.0 + + '@types/estree@1.0.7': {} + + '@types/json-schema@7.0.15': {} + + '@types/react-dom@19.1.2(@types/react@19.1.2)': + dependencies: + '@types/react': 19.1.2 + + '@types/react@19.1.2': + dependencies: + csstype: 3.1.3 + + '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/type-utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.31.1 + eslint: 9.25.1(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.31.1 + debug: 4.4.0 + eslint: 9.25.1(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.31.1': + dependencies: + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/visitor-keys': 8.31.1 + + '@typescript-eslint/type-utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + debug: 4.4.0 + eslint: 9.25.1(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.31.1': {} + + '@typescript-eslint/typescript-estree@8.31.1(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/visitor-keys': 8.31.1 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.7.3) + eslint: 9.25.1(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.31.1': + dependencies: + '@typescript-eslint/types': 8.31.1 + eslint-visitor-keys: 4.2.0 + + '@vitejs/plugin-react@4.4.1(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@babel/core': 7.26.10 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.3.3(jiti@2.4.2)(lightningcss@1.29.2) + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.9.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.2 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001715 + electron-to-chromium: 1.5.144 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.4) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001715: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.0.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + detect-libc@2.0.4: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.144: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.25.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.3 + '@esbuild/android-arm': 0.25.3 + '@esbuild/android-arm64': 0.25.3 + '@esbuild/android-x64': 0.25.3 + '@esbuild/darwin-arm64': 0.25.3 + '@esbuild/darwin-x64': 0.25.3 + '@esbuild/freebsd-arm64': 0.25.3 + '@esbuild/freebsd-x64': 0.25.3 + '@esbuild/linux-arm': 0.25.3 + '@esbuild/linux-arm64': 0.25.3 + '@esbuild/linux-ia32': 0.25.3 + '@esbuild/linux-loong64': 0.25.3 + '@esbuild/linux-mips64el': 0.25.3 + '@esbuild/linux-ppc64': 0.25.3 + '@esbuild/linux-riscv64': 0.25.3 + '@esbuild/linux-s390x': 0.25.3 + '@esbuild/linux-x64': 0.25.3 + '@esbuild/netbsd-arm64': 0.25.3 + '@esbuild/netbsd-x64': 0.25.3 + '@esbuild/openbsd-arm64': 0.25.3 + '@esbuild/openbsd-x64': 0.25.3 + '@esbuild/sunos-x64': 0.25.3 + '@esbuild/win32-arm64': 0.25.3 + '@esbuild/win32-ia32': 0.25.3 + '@esbuild/win32-x64': 0.25.3 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.25.1(jiti@2.4.2)): + dependencies: + eslint: 9.25.1(jiti@2.4.2) + + eslint-plugin-react-refresh@0.4.20(eslint@9.25.1(jiti@2.4.2)): + dependencies: + eslint: 9.25.1(jiti@2.4.2) + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.25.1(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.1 + '@eslint/core': 0.13.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.25.1 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.2 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.9: {} + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@11.12.0: {} + + globals@14.0.0: {} + + globals@15.15.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + jiti@2.4.2: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.19: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + 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@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-refresh@0.17.0: {} + + react-router-dom@7.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-router: 7.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + + react-router@7.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + cookie: 1.0.2 + react: 19.1.0 + set-cookie-parser: 2.7.1 + turbo-stream: 2.4.0 + optionalDependencies: + react-dom: 19.1.0(react@19.1.0) + + react@19.1.0: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rollup@4.40.1: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.1 + '@rollup/rollup-android-arm64': 4.40.1 + '@rollup/rollup-darwin-arm64': 4.40.1 + '@rollup/rollup-darwin-x64': 4.40.1 + '@rollup/rollup-freebsd-arm64': 4.40.1 + '@rollup/rollup-freebsd-x64': 4.40.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.1 + '@rollup/rollup-linux-arm-musleabihf': 4.40.1 + '@rollup/rollup-linux-arm64-gnu': 4.40.1 + '@rollup/rollup-linux-arm64-musl': 4.40.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.1 + '@rollup/rollup-linux-riscv64-gnu': 4.40.1 + '@rollup/rollup-linux-riscv64-musl': 4.40.1 + '@rollup/rollup-linux-s390x-gnu': 4.40.1 + '@rollup/rollup-linux-x64-gnu': 4.40.1 + '@rollup/rollup-linux-x64-musl': 4.40.1 + '@rollup/rollup-win32-arm64-msvc': 4.40.1 + '@rollup/rollup-win32-ia32-msvc': 4.40.1 + '@rollup/rollup-win32-x64-msvc': 4.40.1 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + scheduler@0.26.0: {} + + semver@6.3.1: {} + + semver@7.7.1: {} + + set-cookie-parser@2.7.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tailwindcss@4.1.4: {} + + tapable@2.2.1: {} + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.1.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + turbo-stream@2.4.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.25.1(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + typescript@5.7.3: {} + + update-browserslist-db@1.1.3(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + esbuild: 0.25.3 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.1 + tinyglobby: 0.2.13 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/public/vite.svg" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/public/vite.svg" new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/public/vite.svg" @@ -0,0 +1 @@ + \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/apis/lp.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/apis/lp.ts" new file mode 100644 index 00000000..c7aafaa4 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/apis/lp.ts" @@ -0,0 +1,25 @@ +import { useEffect, useState } from "react"; +import { Movie, MovieResponse } from "../types/movie"; + +import axios from "axios"; + +const MoviesPage = () => { + const [movies, setMovies] = useState([]); + + useEffect(() => { + const fetchMovies = async () => { + // 응답에 대한 타입을 정의해줍니다. + const { data } = await axios.get( + `https://api.themoviedb.org/3/movie/popular?language=ko-KR&page=1` + ); + + setMovies(data.results); + }; + + fetchMovies(); + }, []); + + return <>; +}; + +export default MoviesPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/App.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/App.css" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/App.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/App.tsx" new file mode 100644 index 00000000..00948bcc --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/App.tsx" @@ -0,0 +1,20 @@ +import './App.css'; +import Homepage from './pages/Homepage'; +import MoviePage from './pages/MoviePage'; +import { createBrowserRouter,RouterProvider } from "react-router-dom"; +import NotFoundPage from './pages/NotFoundPage'; +import MovieDetailPage from './pages/MovieDetailPage' + +const router : Router =createBrowserRouter(routes:{ + +}) + +function App() { + return ( +
+ {/* ✅ 이렇게 써줘야 경고 사라짐 */} +
+ ); +} + +export default App; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/index.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/index.css" new file mode 100644 index 00000000..f1d8c73c --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/index.css" @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/main.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/main.tsx" new file mode 100644 index 00000000..10ed13e0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/main.tsx" @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.tsx"; + +createRoot(document.getElementById("root")!).render( + + + +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/react.svg" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/react.svg" new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/react.svg" @@ -0,0 +1 @@ + \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/vite-env.d.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/vite-env.d.ts" new file mode 100644 index 00000000..af380ea0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/assets/vite-env.d.ts" @@ -0,0 +1,8 @@ +/// +import { defineConfig } from "vite"; +import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react(), tailwindcss()], +}); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/alert/alert.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/alert/alert.tsx" new file mode 100644 index 00000000..f3315140 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/alert/alert.tsx" @@ -0,0 +1,7 @@ +const Alert = ({onclose}): { onClose: () ==> void }) => {} + return( + +
+
+
+} \ No newline at end of file diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/movieCard.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/movieCard.tsx" new file mode 100644 index 00000000..46258fbe --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/movieCard.tsx" @@ -0,0 +1,29 @@ +export type TMovie = { + adult: boolean; + title: string; + overview: string; + vote_count: number; + poster_path: string; + id: number; +}; + +const MovieCard = (movie: TMovie) => { + return ( +
+ {movie.title} +
+
{movie.title}
+
{movie.overview}
+
⭐ {movie.vote_count}
+
+
+ ); +}; + +// 카드 크기는 부모의 `grid` 설정에 따라 자동 조정 + +export default MovieCard; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/navbar.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/navbar.tsx" new file mode 100644 index 00000000..7a57152d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/components/navbar.tsx" @@ -0,0 +1,55 @@ +import { NavLink } from "react-router-dom"; + +const Navbar = () => { + const baseClass = "px-4 py-2 transition-colors duration-200"; + const activeClass = "text-blue-500 font-semibold"; + const inactiveClass = "text-gray-700 hover:text-black"; + + return ( +
+ + `${baseClass} ${isActive ? activeClass : inactiveClass}` + } + > + 홈 + + + `${baseClass} ${isActive ? activeClass : inactiveClass}` + } + > + 인기 영화 + + + `${baseClass} ${isActive ? activeClass : inactiveClass}` + } + > + 상영 중 + + + `${baseClass} ${isActive ? activeClass : inactiveClass}` + } + > + 평점 높은 + + + `${baseClass} ${isActive ? activeClass : inactiveClass}` + } + > + 개봉 예정 + +
+ ); +}; + +export default Navbar; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/layout/layout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/layout/layout.tsx" new file mode 100644 index 00000000..f521ac1d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/layout/layout.tsx" @@ -0,0 +1,13 @@ +import { Outlet } from "react-router-dom"; +import Navbar from "../components/navbar"; + +const Layout = () => { + return ( +
+ + +
+ ); +}; + +export default Layout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/loading/loading.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/loading/loading.tsx" new file mode 100644 index 00000000..a0fbb52e --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/loading/loading.tsx" @@ -0,0 +1,9 @@ +const Loading = () => { + return ( +
+
+
+ ); +}; + +export default Loading; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/Homepage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/Homepage.tsx" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/MovieDetailPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/MovieDetailPage.tsx" new file mode 100644 index 00000000..c246acef --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/MovieDetailPage.tsx" @@ -0,0 +1,10 @@ +import { useParams } from "react-router-dom"; + +const MovieDetailPage = () => { + const params = useParams(); + + console.log(params); + return
MovieDetailPage(params.movieid)
; +}; + +export default MovieDetailPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/MoviePage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/MoviePage.tsx" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/home.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/home.tsx" new file mode 100644 index 00000000..86307fbf --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/home.tsx" @@ -0,0 +1,18 @@ +import axios from "axios"; +import { useEffect, useState } from "react"; +import { TMovie } from "../components/movieCard"; +import MovieCard from "../components/movieCard"; + +// import { + return ( +
+ {data.map((movie: TMovie, idx) => { + return ; + })} + ; +
+ ); +}; +export default Home; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/movies.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/movies.tsx" new file mode 100644 index 00000000..c737f419 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/pages/movies.tsx" @@ -0,0 +1,64 @@ +import { useEffect, useState } from "react"; +import MovieCard from "../components/movieCard"; +import axios from "axios"; +import Loading from "../loading/loading"; + +export type TMovie = { + adult: boolean; + title: string; + overview: string; + vote_count: number; + poster_path: string; + id: number; +}; + +const Movies = () => { + const params = document.location.pathname.split("/")[1]; + + const [isLoading, setIsLoading] = useState(true); // ✅ 선언 먼저! + const [data, setData] = useState([]); + + useEffect(() => { + const fetchMovies = async () => { + setIsLoading(true); + try { + const response = await axios( + `https://api.themoviedb.org/3/movie/${params}?language=ko-KR&page=100`, + { + headers: { + Authorization: `Bearer ${import.meta.env.VITE_TMDB_TOKEN}`, + }, + } + ); + setData(response.data.results); + } catch (error) { + console.error("데이터 요청 에러:", error); + } finally { + setIsLoading(false); + } + }; + + fetchMovies(); + }, [params]); + + if (isLoading) { + return ; + } + return ( +
+
+ {data.map((movie: TMovie, idx) => ( + + ))} + + {/* {!isLoading && } */} +
+ +
+ +
+
+ ); +}; + +export default Movies; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/routes/routes.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/routes/routes.tsx" new file mode 100644 index 00000000..761ccc0e --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/routes/routes.tsx" @@ -0,0 +1,40 @@ +import { createBrowserRouter } from "react-router-dom"; +import Home from "../pages/home"; +import Layout from "../layout/layout"; +import Movies from "../pages/movies"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + errorElement:

error

, + children: [ + { + index: true, + element: , + }, + { + path: "/popular", + element: , + }, + { + path: "/upcoming", + element: , + }, + { + path: "/now_playing", + element: , + }, + { + path: "/top_rated", + element: , + }, + ], + }, + // { + // path: "/movies", + // element: , + // }, +]); + +export default router; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/types/data.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/src/types/data.ts" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.app.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.app.json" new file mode 100644 index 00000000..358ca9ba --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.app.json" @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.json" new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.json" @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.node.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.node.json" new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/tsconfig.node.json" @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/vite.config.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/vite.config.ts" new file mode 100644 index 00000000..3e8f4fb8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/vite.config.ts" @@ -0,0 +1,5 @@ +import { defineConfig } from "vite"; +import tailwindcss from "@tailwindcss/vite"; +export default defineConfig({ + plugins: [tailwindcss()], +}); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/yarn.lock" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/yarn.lock" new file mode 100644 index 00000000..de23000a --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/6\354\243\274\354\260\250_\353\257\270\354\205\230/mission1/yarn.lock" @@ -0,0 +1,1611 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.8": + version "7.26.8" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.26.0": + version "7.26.10" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.10", "@babel/generator@^7.27.0": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz" + integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== + dependencies: + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz" + integrity sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA== + dependencies: + "@babel/compat-data" "^7.26.8" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.10": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== + dependencies: + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz" + integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== + dependencies: + "@babel/types" "^7.27.0" + +"@babel/plugin-transform-react-jsx-self@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz" + integrity sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-jsx-source@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz" + integrity sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.26.9", "@babel/template@^7.27.0": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz" + integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz" + integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.27.0" + "@babel/parser" "^7.27.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@esbuild/win32-x64@0.25.2": + version "0.25.2" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz" + integrity sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.5.1" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz" + integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.20.0": + version "0.20.0" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz" + integrity sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.2.0": + version "0.2.1" + resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz" + integrity sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw== + +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/core@^0.13.0": + version "0.13.0" + resolved "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz" + integrity sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.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" + +"@eslint/js@^9.21.0", "@eslint/js@9.24.0": + version "9.24.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz" + integrity sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.7": + version "0.2.8" + resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz" + integrity sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA== + dependencies: + "@eslint/core" "^0.13.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + 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@^1.2.1": + 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@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + 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@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + 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" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@rollup/rollup-win32-x64-msvc@4.39.0": + version "4.39.0" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz" + integrity sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug== + +"@tailwindcss/node@4.1.3": + version "4.1.3" + resolved "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz" + integrity sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA== + dependencies: + enhanced-resolve "^5.18.1" + jiti "^2.4.2" + lightningcss "1.29.2" + tailwindcss "4.1.3" + +"@tailwindcss/oxide-win32-x64-msvc@4.1.3": + version "4.1.3" + resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz" + integrity sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA== + +"@tailwindcss/oxide@4.1.3": + version "4.1.3" + resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz" + integrity sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ== + optionalDependencies: + "@tailwindcss/oxide-android-arm64" "4.1.3" + "@tailwindcss/oxide-darwin-arm64" "4.1.3" + "@tailwindcss/oxide-darwin-x64" "4.1.3" + "@tailwindcss/oxide-freebsd-x64" "4.1.3" + "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.3" + "@tailwindcss/oxide-linux-arm64-gnu" "4.1.3" + "@tailwindcss/oxide-linux-arm64-musl" "4.1.3" + "@tailwindcss/oxide-linux-x64-gnu" "4.1.3" + "@tailwindcss/oxide-linux-x64-musl" "4.1.3" + "@tailwindcss/oxide-win32-arm64-msvc" "4.1.3" + "@tailwindcss/oxide-win32-x64-msvc" "4.1.3" + +"@tailwindcss/vite@^4.1.3": + version "4.1.3" + resolved "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.3.tgz" + integrity sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ== + dependencies: + "@tailwindcss/node" "4.1.3" + "@tailwindcss/oxide" "4.1.3" + tailwindcss "4.1.3" + +"@tanstack/query-core@5.74.7": + version "5.74.7" + resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.74.7.tgz" + integrity sha512-X3StkN/Y6KGHndTjJf8H8th7AX4bKfbRpiVhVqevf0QWlxl6DhyJ0TYG3R0LARa/+xqDwzU9mA4pbJxzPCI29A== + +"@tanstack/query-devtools@5.74.7": + version "5.74.7" + resolved "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.74.7.tgz" + integrity sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw== + +"@tanstack/react-query-devtools@^5.74.8": + version "5.74.8" + resolved "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.74.8.tgz" + integrity sha512-wNxQ91Ygg5cJJIE8zau18zaU9v3ZA42k9VpEoHNB+n33ntQIOVEBhE84uWrou6utShd21EQUyW0q/viAUR20MA== + dependencies: + "@tanstack/query-devtools" "5.74.7" + +"@tanstack/react-query@^5.74.8": + version "5.74.8" + resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.74.8.tgz" + integrity sha512-Hs3QHLYyyc/yUI8tS7CIM8vRmlm+2gPBiNsHzcDclvqZNCgTc0LdXRrZDhSxxhcnXJrIMnIcoNPwj9BshsCT+Q== + dependencies: + "@tanstack/query-core" "5.74.7" + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + dependencies: + "@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== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.7" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz" + integrity sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng== + dependencies: + "@babel/types" "^7.20.7" + +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + +"@types/estree@^1.0.6", "@types/estree@1.0.7": + version "1.0.7" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/react-dom@^19.0.4": + version "19.1.1" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz" + integrity sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w== + +"@types/react@^19.0.0", "@types/react@^19.0.10": + version "19.1.0" + resolved "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz" + integrity sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w== + dependencies: + csstype "^3.0.2" + +"@typescript-eslint/eslint-plugin@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz" + integrity sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/type-utils" "8.29.0" + "@typescript-eslint/utils" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/parser@^8.0.0 || ^8.0.0-alpha.0", "@typescript-eslint/parser@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz" + integrity sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g== + dependencies: + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/typescript-estree" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz" + integrity sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw== + dependencies: + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + +"@typescript-eslint/type-utils@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz" + integrity sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q== + dependencies: + "@typescript-eslint/typescript-estree" "8.29.0" + "@typescript-eslint/utils" "8.29.0" + debug "^4.3.4" + ts-api-utils "^2.0.1" + +"@typescript-eslint/types@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz" + integrity sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg== + +"@typescript-eslint/typescript-estree@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz" + integrity sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow== + dependencies: + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/visitor-keys" "8.29.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/utils@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz" + integrity sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.29.0" + "@typescript-eslint/types" "8.29.0" + "@typescript-eslint/typescript-estree" "8.29.0" + +"@typescript-eslint/visitor-keys@8.29.0": + version "8.29.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz" + integrity sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg== + dependencies: + "@typescript-eslint/types" "8.29.0" + eslint-visitor-keys "^4.2.0" + +"@vitejs/plugin-react@^4.3.4": + version "4.3.4" + resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz" + integrity sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug== + dependencies: + "@babel/core" "^7.26.0" + "@babel/plugin-transform-react-jsx-self" "^7.25.9" + "@babel/plugin-transform-react-jsx-source" "^7.25.9" + "@types/babel__core" "^7.20.5" + react-refresh "^0.14.2" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.14.0: + version "8.14.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +ansi-styles@^4.1.0: + 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" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.8.4: + version "1.8.4" + resolved "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz" + integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.24.0, "browserslist@>= 4.21.0": + version "4.24.4" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001688: + version "1.0.30001712" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz" + integrity sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + 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" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + 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" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^2.0.0: + 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== + +cookie@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz" + integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.4.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + 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== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +electron-to-chromium@^1.5.73: + version "1.5.132" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz" + integrity sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg== + +enhanced-resolve@^5.18.1: + version "5.18.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz" + integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +esbuild@^0.25.0: + version "0.25.2" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz" + integrity sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.2" + "@esbuild/android-arm" "0.25.2" + "@esbuild/android-arm64" "0.25.2" + "@esbuild/android-x64" "0.25.2" + "@esbuild/darwin-arm64" "0.25.2" + "@esbuild/darwin-x64" "0.25.2" + "@esbuild/freebsd-arm64" "0.25.2" + "@esbuild/freebsd-x64" "0.25.2" + "@esbuild/linux-arm" "0.25.2" + "@esbuild/linux-arm64" "0.25.2" + "@esbuild/linux-ia32" "0.25.2" + "@esbuild/linux-loong64" "0.25.2" + "@esbuild/linux-mips64el" "0.25.2" + "@esbuild/linux-ppc64" "0.25.2" + "@esbuild/linux-riscv64" "0.25.2" + "@esbuild/linux-s390x" "0.25.2" + "@esbuild/linux-x64" "0.25.2" + "@esbuild/netbsd-arm64" "0.25.2" + "@esbuild/netbsd-x64" "0.25.2" + "@esbuild/openbsd-arm64" "0.25.2" + "@esbuild/openbsd-x64" "0.25.2" + "@esbuild/sunos-x64" "0.25.2" + "@esbuild/win32-arm64" "0.25.2" + "@esbuild/win32-ia32" "0.25.2" + "@esbuild/win32-x64" "0.25.2" + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-react-hooks@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz" + integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg== + +eslint-plugin-react-refresh@^0.4.19: + version "0.4.19" + resolved "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz" + integrity sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ== + +eslint-scope@^8.3.0: + version "8.3.0" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz" + integrity sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + 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== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +"eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9.21.0, eslint@>=8.40: + version "9.24.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz" + integrity sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.20.0" + "@eslint/config-helpers" "^0.2.0" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.24.0" + "@eslint/plugin-kit" "^0.2.7" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.3.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + 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== + +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + 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.8" + +fast-json-stable-stringify@^2.0.0: + 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== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +form-data@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + mime-types "^2.1.12" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + 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== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.15.0: + version "15.15.0" + resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.2.4: + 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== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + 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" + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jiti@*, jiti@^2.4.2, jiti@>=1.21.0: + version "2.4.2" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz" + integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + 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== + +json-stable-stringify-without-jsonify@^1.0.1: + 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== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lightningcss-win32-x64-msvc@1.29.2: + version "1.29.2" + resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz" + integrity sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA== + +lightningcss@^1.21.0, lightningcss@1.29.2: + version "1.29.2" + resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz" + integrity sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-darwin-arm64 "1.29.2" + lightningcss-darwin-x64 "1.29.2" + lightningcss-freebsd-x64 "1.29.2" + lightningcss-linux-arm-gnueabihf "1.29.2" + lightningcss-linux-arm64-gnu "1.29.2" + lightningcss-linux-arm64-musl "1.29.2" + lightningcss-linux-x64-gnu "1.29.2" + lightningcss-linux-x64-musl "1.29.2" + lightningcss-win32-arm64-msvc "1.29.2" + lightningcss-win32-x64-msvc "1.29.2" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + 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" + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.8: + version "3.3.11" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + 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" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + 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== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + 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" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postcss@^8.5.3: + version "8.5.3" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +react-dom@^19.0.0, react-dom@>=18: + version "19.1.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz" + integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== + dependencies: + scheduler "^0.26.0" + +react-refresh@^0.14.2: + version "0.14.2" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== + +react-router-dom@^7.5.0: + version "7.5.0" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.0.tgz" + integrity sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA== + dependencies: + react-router "7.5.0" + +react-router@7.5.0: + version "7.5.0" + resolved "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz" + integrity sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g== + dependencies: + "@types/cookie" "^0.6.0" + cookie "^1.0.1" + set-cookie-parser "^2.6.0" + turbo-stream "2.4.0" + +"react@^18 || ^19", react@^19.0.0, react@^19.1.0, react@>=18: + version "19.1.0" + resolved "https://registry.npmjs.org/react/-/react-19.1.0.tgz" + integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +rollup@^4.30.1: + version "4.39.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz" + integrity sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g== + dependencies: + "@types/estree" "1.0.7" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.39.0" + "@rollup/rollup-android-arm64" "4.39.0" + "@rollup/rollup-darwin-arm64" "4.39.0" + "@rollup/rollup-darwin-x64" "4.39.0" + "@rollup/rollup-freebsd-arm64" "4.39.0" + "@rollup/rollup-freebsd-x64" "4.39.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.39.0" + "@rollup/rollup-linux-arm-musleabihf" "4.39.0" + "@rollup/rollup-linux-arm64-gnu" "4.39.0" + "@rollup/rollup-linux-arm64-musl" "4.39.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.39.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.39.0" + "@rollup/rollup-linux-riscv64-gnu" "4.39.0" + "@rollup/rollup-linux-riscv64-musl" "4.39.0" + "@rollup/rollup-linux-s390x-gnu" "4.39.0" + "@rollup/rollup-linux-x64-gnu" "4.39.0" + "@rollup/rollup-linux-x64-musl" "4.39.0" + "@rollup/rollup-win32-arm64-msvc" "4.39.0" + "@rollup/rollup-win32-ia32-msvc" "4.39.0" + "@rollup/rollup-win32-x64-msvc" "4.39.0" + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +scheduler@^0.26.0: + version "0.26.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz" + integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.6.0: + version "7.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +set-cookie-parser@^2.6.0: + version "2.7.1" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz" + integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +strip-json-comments@^3.1.1: + 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== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +tailwindcss@^4.1.3, tailwindcss@4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz" + integrity sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g== + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +to-regex-range@^5.0.1: + 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== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^2.0.1: + version "2.1.0" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +turbo-stream@2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz" + integrity sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +typescript-eslint@^8.24.1: + version "8.29.0" + resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz" + integrity sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg== + dependencies: + "@typescript-eslint/eslint-plugin" "8.29.0" + "@typescript-eslint/parser" "8.29.0" + "@typescript-eslint/utils" "8.29.0" + +typescript@>=4.8.4, "typescript@>=4.8.4 <5.9.0", typescript@~5.7.2: + version "5.7.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== + +update-browserslist-db@^1.1.1: + version "1.1.3" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +"vite@^4.2.0 || ^5.0.0 || ^6.0.0", "vite@^5.2.0 || ^6", vite@^6.2.0: + version "6.2.5" + resolved "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz" + integrity sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA== + dependencies: + esbuild "^0.25.0" + postcss "^8.5.3" + rollup "^4.30.1" + optionalDependencies: + fsevents "~2.3.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/index.html" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/index.html" new file mode 100644 index 00000000..e0d1c840 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/index.html" @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/package.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/package.json" new file mode 100644 index 00000000..e3b62c93 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/package.json" @@ -0,0 +1,33 @@ +{ + "name": "week4-mission2", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@tailwindcss/vite": "^4.1.6", + "axios": "^1.9.0", + "react-hook-form": "^7.56.3", + "react-router-dom": "^7.6.0", + "tailwindcss": "^4.1.6", + "zod": "^3.24.4" + }, + "description": "", + "devDependencies": { + "@types/axios": "^0.9.36", + "@types/node": "^22.15.17", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.4", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.4.1", + "vite": "^6.3.5" + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/pnpm-lock.yaml" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/pnpm-lock.yaml" new file mode 100644 index 00000000..0721ff56 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/pnpm-lock.yaml" @@ -0,0 +1,1002 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.1.6 + version: 4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)) + tailwindcss: + specifier: ^4.1.6 + version: 4.1.6 + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.1.6': + resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==} + + '@tailwindcss/oxide-android-arm64@4.1.6': + resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.6': + resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.6': + resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.6': + resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': + resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': + resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': + resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': + resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.6': + resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.6': + resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': + resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': + resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.6': + resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.6': + resolution: {integrity: sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tailwindcss@4.1.6: + resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@rollup/rollup-android-arm-eabi@4.40.2': + optional: true + + '@rollup/rollup-android-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-x64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.2': + optional: true + + '@tailwindcss/node@4.1.6': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.29.2 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.6 + + '@tailwindcss/oxide-android-arm64@4.1.6': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.6': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.6': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.6': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.6': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': + optional: true + + '@tailwindcss/oxide@4.1.6': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.6 + '@tailwindcss/oxide-darwin-arm64': 4.1.6 + '@tailwindcss/oxide-darwin-x64': 4.1.6 + '@tailwindcss/oxide-freebsd-x64': 4.1.6 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.6 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.6 + '@tailwindcss/oxide-linux-x64-musl': 4.1.6 + '@tailwindcss/oxide-wasm32-wasi': 4.1.6 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.6 + + '@tailwindcss/vite@4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@tailwindcss/node': 4.1.6 + '@tailwindcss/oxide': 4.1.6 + tailwindcss: 4.1.6 + vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) + + '@types/estree@1.0.7': {} + + chownr@3.0.0: {} + + detect-libc@2.0.4: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + jiti@2.4.2: {} + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + + nanoid@3.3.11: {} + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rollup@4.40.2: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 + fsevents: 2.3.3 + + source-map-js@1.2.1: {} + + tailwindcss@4.1.6: {} + + tapable@2.2.1: {} + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + esbuild: 0.25.4 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.2 + tinyglobby: 0.2.13 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + + yallist@5.0.0: {} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/postcss.config.cjs" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/postcss.config.cjs" new file mode 100644 index 00000000..1719dfd2 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/postcss.config.cjs" @@ -0,0 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/App.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/App.css" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/App.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/App.tsx" new file mode 100644 index 00000000..b3bd3faf --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/App.tsx" @@ -0,0 +1,51 @@ +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import LoginPage from "./pages/LoginPage"; +import HomeLayout from "./layouts/HomeLayout"; +import HomePage from "./pages/HomePage"; +import SignupPage from "./pages/SignupPage"; +import NotFoundPage from "./pages/NotFoundPage"; +import MyPage from "./pages/MyPage"; +import { AuthProvider } from "./context/AuthContext"; +import { RouteObject } from "react-router-dom"; +import ProtectedLayout from "./layouts/ProtectedLayout"; + +// 1. 홈페이지 +// 2. 로그인 페이지 +// 3. 회원가입 페이지 + +//publicRoutes : 인증 없이 접근 가능한 라우트 +const publicRoutes: RouteObject[] = [ + { + path: "/", + element: , + errorElement: , + children: [ + { index: true, element: }, + { path: "login", element: }, + { path: "signup", element: }, + { path: "v1/auth/google/callback", element: }, + ], + }, +]; + +//protectedRoutes : 인증이 필요한 라우트 +const protectedRoutes: RouteObject[] = [ + { + path: "/my-page", + element: , + errorElement: , + children: [{ element: }], + }, +]; + +const router = createBrowserRouter([...publicRoutes, ...protectedRoutes]); + +function App() { + return ( + + + + ); +} + +export default App; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/auth.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/auth.ts" new file mode 100644 index 00000000..f14d2220 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/auth.ts" @@ -0,0 +1,57 @@ +import { axiosInstance } from "./axios"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; +import { ResponseMyInfoDto } from "../types/auth"; + +export async function postSignup(data: { + email: string; + password: string; + name: string; +}) { + try { + const response = await axiosInstance.post("v1/auth/signup", data); + return response.data; + } catch (error) { + console.error("Signup Error:", error); + throw error; + } +} + +export async function postSignin(data: { email: string; password: string }) { + try { + const response = await axiosInstance.post("v1/auth/signin", data); + return response.data; + } catch (error) { + console.error("Signin Error:", error); + throw error; + } +} + +export async function getMyInfo(): Promise { + const token = localStorage.getItem(LOCAL_STORAGE_KEY.accessToken); + try { + const response = await axiosInstance.get("v1/users/me", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + console.error("getMyInfo Error:", error); + throw error; + } +} + +export const postLogout = async () => { + const token = localStorage.getItem(LOCAL_STORAGE_KEY.accessToken); + try { + const response = await axiosInstance.post("v1/auth/signout", null, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + console.error("Logout Error:", error); + throw error; + } +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/axios.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/axios.ts" new file mode 100644 index 00000000..884ac912 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/apis/axios.ts" @@ -0,0 +1,110 @@ +import axios, { AxiosRequestConfig, InternalAxiosRequestConfig } from "axios"; +import { LOCAL_STORAGE_KEY } from "../constants/key.ts"; +import { useLocalStorage } from "../hooks/useLocalStorage"; + +console.log("VITE_SERVER_API_URL:", import.meta.env.VITE_SERVER_API_URL); + +interface CustomAxiosRequestConfig extends AxiosRequestConfig { + _retry?: boolean; +} + +let refreshPromise: Promise | null = null; + +export const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_SERVER_API_URL, +}); + +// 요청 인터셉터 +axiosInstance.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const { getItem } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + const accessToken = getItem(); + + // accessToken이 존재하면 Authorization 헤더에 Bearer 토큰 형식으로 추가한다. + if (accessToken) { + config.headers = config.headers || {}; + config.headers["Authorization"] = `Bearer ${accessToken}`; + } + + return config; + }, + (error) => Promise.reject(error) +); + +// 응답 인터셉터 +axiosInstance.interceptors.response.use( + (response) => response, //정상 응답 그대로 반환환 + async (error) => { + const originalRequest = error.config as CustomAxiosRequestConfig; + + if ( + error.response && + error.response.status === 401 && + !originalRequest._retry + ) { + if (originalRequest.url === "/v1/auth/refresh") { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + removeAccessToken(); + removeRefreshToken(); + window.location.href = "/login"; + return Promise.reject(error); + } + + originalRequest._retry = true; + + if (!refreshPromise) { + refreshPromise = (async () => { + const { getItem: getRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + const refreshToken = getRefreshToken(); + + const { data } = await axiosInstance.post("/v1/auth/refresh", { + refresh: refreshToken, + }); + + const { setItem: setAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { setItem: setRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + setAccessToken(data.data.accessToken); + setRefreshToken(data.data.refreshToken); + + return data.data.accessToken; + })() + .catch(() => { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + removeAccessToken(); + removeRefreshToken(); + }) + .finally(() => { + refreshPromise = null; + }); + } + + return refreshPromise.then((newAccessToken) => { + if (originalRequest.headers) { + originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; + } + return axiosInstance(originalRequest); + }); + } + + return Promise.reject(error); + } +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/assets/react.svg" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/assets/react.svg" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/components/Navbar.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/components/Navbar.tsx" new file mode 100644 index 00000000..b3160ef9 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/components/Navbar.tsx" @@ -0,0 +1,57 @@ +import { Link } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; + +const Navbar = () => { + const { accessToken, logout } = useAuth(); + + return ( + + ); +}; + +export default Navbar; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/constants/key.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/constants/key.ts" new file mode 100644 index 00000000..4b6c9308 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/constants/key.ts" @@ -0,0 +1,5 @@ +export const LOCAL_STORAGE_KEY: { accessToken: string; refreshToken: string } = + { + accessToken: "accessToken", + refreshToken: "refreshToken", + }; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/context/AuthContext.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/context/AuthContext.tsx" new file mode 100644 index 00000000..9be3bb22 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/context/AuthContext.tsx" @@ -0,0 +1,113 @@ +import { useLocalStorage } from "../hooks/useLocalStorage"; +import { RequestSigninDto } from "../types/auth"; +import { + createContext, + PropsWithChildren, + useState, + useContext, + useEffect, +} from "react"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; +import { postSignin, postLogout } from "../apis/auth"; + +interface AuthContextType { + accessToken: string | null; + refreshToken: string | null; + login: (signinData: RequestSigninDto) => Promise; + logout: () => Promise; +} + +export const AuthContext: React.Context = + createContext({ + accessToken: null, + refreshToken: null, + login: async () => {}, + logout: async () => {}, + }); + +export const AuthProvider = ({ children }: PropsWithChildren) => { + const { + getItem: getAccessTokenFromStorage, + setItem: setAccessTokenInStorage, + removeItem: removeAccessTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + + const { + getItem: getRefreshTokenFromStorage, + setItem: setRefreshTokenInStorage, + removeItem: removeRefreshTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.refreshToken); + + const [accessToken, setAccessToken] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + + useEffect(() => { + const storedAccessToken = getAccessTokenFromStorage(); + const storedRefreshToken = getRefreshTokenFromStorage(); + + if (storedAccessToken && storedRefreshToken) { + setAccessToken(storedAccessToken); + setRefreshToken(storedRefreshToken); + } else { + setAccessToken(null); + setRefreshToken(null); + } + }, []); + + const login = async (signinData: RequestSigninDto) => { + try { + console.log("로그인 시도:", signinData); + const response = await postSignin(signinData); + console.log("로그인 응답:", response); + + if (response && response.data) { + const { accessToken, refreshToken } = response.data; + console.log("토큰 정보:", { accessToken, refreshToken }); + + setAccessToken(accessToken); + setRefreshToken(refreshToken); + setAccessTokenInStorage(accessToken); + setRefreshTokenInStorage(refreshToken); + + console.log("토큰 저장 완료"); + alert("로그인 성공"); + window.location.href = "/my-page"; + } else { + console.error("로그인 응답이 올바르지 않습니다:", response); + alert("로그인 실패: 응답이 올바르지 않습니다"); + } + } catch (error) { + console.error("로그인 오류:", error); + alert("로그인 실패"); + } + }; + + const logout = async () => { + try { + const token = getAccessTokenFromStorage(); + if (token) { + await postLogout(); + } + removeAccessTokenFromStorage(); + removeRefreshTokenFromStorage(); + setAccessToken(null); + setRefreshToken(null); + alert("로그아웃 성공"); + window.location.href = "/"; + } catch (error) { + console.error("로그아웃 오류", error); + alert("로그아웃 실패"); + } + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + return context; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useForms.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useForms.ts" new file mode 100644 index 00000000..6ddab44b --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useForms.ts" @@ -0,0 +1,53 @@ +import { useState, useEffect } from "react"; + +interface UseFormProps { + initialValues: T; + validate: (values: T) => Partial; + onSubmit: (values: T) => void; +} + +function useForm({ initialValues, validate, onSubmit }: UseFormProps) { + const [values, setValues] = useState(initialValues); + const [touched, setTouched] = useState>( + {} as Record + ); + const [errors, setErrors] = useState>({}); + + const handleChange = (name: keyof T, text: string) => { + setValues({ + ...values, + [name]: text, + }); + }; + + const handleBlur = (name: keyof T) => { + setTouched({ + ...touched, + [name]: true, + }); + }; + + const getInputProps = (name: keyof T) => { + const value = values[name]; + const onChange = ( + e: React.ChangeEvent + ) => handleChange(name, e.target.value); + const onBlur = () => handleBlur(name); + + return { value, onChange, onBlur }; + }; + + useEffect(() => { + const newErrors = validate(values); + setErrors(newErrors); + }, [validate, values]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + onSubmit(values); + }; + + return { errors, touched, getInputProps, handleSubmit, values }; +} + +export default useForm; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useLocalStorage.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useLocalStorage.ts" new file mode 100644 index 00000000..2bede60f --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/hooks/useLocalStorage.ts" @@ -0,0 +1,27 @@ +export const useLocalStorage = (key: string) => { + const setItem = (value: unknown) => { + try { + window.localStorage.setItem(key, JSON.stringify(value)); + } catch (error) { + console.error(error); + } + }; + + const getItem = () => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : null; + } catch (error) { + console.error(error); + } + }; + + const removeItem = () => { + try { + window.localStorage.removeItem(key); + } catch (error) { + console.error(error); + } + }; + return { setItem, getItem, removeItem }; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/index.css" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/index.css" new file mode 100644 index 00000000..f1d8c73c --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/index.css" @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/HomeLayout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/HomeLayout.tsx" new file mode 100644 index 00000000..a8297440 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/HomeLayout.tsx" @@ -0,0 +1,15 @@ +import { Outlet } from "react-router-dom"; +import Navbar from "../components/Navbar"; + +const HomeLayout = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default HomeLayout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/ProtectedLayout.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/ProtectedLayout.tsx" new file mode 100644 index 00000000..40088fca --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/layouts/ProtectedLayout.tsx" @@ -0,0 +1,13 @@ +import { useAuth } from "../context/AuthContext"; +import { Navigate, Outlet } from "react-router-dom"; + +const ProtectedLayout = () => { + const { accessToken } = useAuth(); + + if (!accessToken) { + return ; + } + return ; +}; + +export default ProtectedLayout; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/main.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/main.tsx" new file mode 100644 index 00000000..763cea77 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/main.tsx" @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; // React의 StrictMode 컴포넌트 import +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; // App 컴포넌트 import + +createRoot(document.getElementById("root")!).render( + + + +); diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/GoogleLoginRedirectPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/GoogleLoginRedirectPage.tsx" new file mode 100644 index 00000000..8c3f752a --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/GoogleLoginRedirectPage.tsx" @@ -0,0 +1,22 @@ +import { useEffect } from "react"; + +import { LOCAL_STORAGE_KEY } from "../constants/localStorage"; + +function GoogleLoginRedirectPage() { + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + + const accessToken = urlParams.get(LOCAL_STORAGE_KEY.accessToken); + const refreshToken = urlParams.get(LOCAL_STORAGE_KEY.refreshToken); + + if (accessToken) { + setAccessToken(accessToken); + setRefreshToken(refreshToken); + window.location.href = "/my-page"; + } + }, [setAccessToken, setRefreshToken]); + + return
구글 로그인 리다이렉트 화면
; +} + +export default GoogleLoginRedirectPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/HomePage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/HomePage.tsx" new file mode 100644 index 00000000..d5a244d2 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/HomePage.tsx" @@ -0,0 +1,10 @@ +function HomePage() { + return ( +
+

홈페이지

+

환영합니다!

+
+ ); +} + +export default HomePage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/LoginPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/LoginPage.tsx" new file mode 100644 index 00000000..e69de29b diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/MyPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/MyPage.tsx" new file mode 100644 index 00000000..5f9025b5 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/MyPage.tsx" @@ -0,0 +1,98 @@ +import { useEffect, useState } from "react"; +import { getMyInfo } from "../apis/auth"; +import { ResponseMyInfoDto } from "../types/auth"; +import { useNavigate } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; + +const MyPage = () => { + const navigate = useNavigate(); + const { logout } = useAuth(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const getData = async () => { + try { + setLoading(true); + const response = await getMyInfo(); + console.log("MyInfo Response:", response); + setData(response); + setError(null); + } catch (error) { + console.error("Failed to fetch user info:", error); + setError("사용자 정보를 가져오는데 실패했습니다."); + } finally { + setLoading(false); + } + }; + getData(); + }, []); + + const handleLogout = async () => { + await logout(); + navigate("/"); + }; + + if (loading) { + return ( +
+

로딩 중...

+
+ ); + } + + if (error) { + return ( +
+

{error}

+ +
+ ); + } + + if (!data || !data.data) { + return ( +
+

사용자 정보가 없습니다.

+ +
+ ); + } + + return ( +
+
+

+ {data.data.name}님 환영합니다. +

+ {data.data.avatar && ( + 프로필 이미지 + )} +

{data.data.email}

+ +
+
+ ); +}; + +export default MyPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/NotFoundPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/NotFoundPage.tsx" new file mode 100644 index 00000000..91f72221 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/NotFoundPage.tsx" @@ -0,0 +1,10 @@ +function NotFoundPage() { + return ( +
+

404

+

페이지를 찾을 수 없습니다.

+
+ ); +} + +export default NotFoundPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/SignupPage.tsx" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/SignupPage.tsx" new file mode 100644 index 00000000..ea744990 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/pages/SignupPage.tsx" @@ -0,0 +1,165 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Link } from "react-router-dom"; +import { postSignup } from "../apis/auth"; +import { AxiosError } from "axios"; +import { SignupForm, signupSchema } from "../utils/schema"; + +function SignupPage() { + const [showPassword, setShowPassword] = useState(false); + const [showPasswordConfirm, setShowPasswordConfirm] = useState(false); + const [isComplete, setIsComplete] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(signupSchema), + mode: "onChange", + }); + + const onSubmit = async (data: SignupForm) => { + console.log("회원가입 성공:", data); + try { + const response = await postSignup({ + email: data.email, + password: data.password, + name: data.nickname, + }); + + if (response) { + setIsComplete(true); + } else alert("회원가입에 실패했습니다."); + } catch (error) { + if (error instanceof AxiosError) { + alert(error.response?.data?.message || "회원가입에 실패했습니다."); + } else alert("알 수 없는 오류가 발생했습니다."); + } + }; + + return ( +
+
+
+ + < + +

회원가입

+
+ + {!isComplete ? ( + <> + +
+
+
+
+
+ OR +
+
+ +
+
+ + {errors.email && ( +
+ {errors.email.message} +
+ )} +
+ +
+ + + {errors.password && ( +
+ {errors.password.message} +
+ )} +
+ +
+ + + {errors.passwordConfirm && ( +
+ {errors.passwordConfirm.message} +
+ )} +
+ +
+ + {errors.nickname && ( +
+ {errors.nickname.message} +
+ )} +
+ + +
+ + ) : ( +
+
+ +
+

회원가입 완료!

+ + 로그인하기 + +
+ )} +
+
+ ); +} + +export default SignupPage; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/auth.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/auth.ts" new file mode 100644 index 00000000..f233a2e7 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/auth.ts" @@ -0,0 +1,41 @@ +import { CommonResponse } from "./common"; + +export type RequestSignupDto = { + name: string; + email: string; + bio?: string; + avatar?: string; + password: string; +}; + +export type ResponseSignupDto = CommonResponse<{ + id: number; + name: string; + email: string; + bio: string | null; + avatar: string | null; + createAt: string; + updateAt: string; +}>; + +export type RequestSigninDto = { + email: string; + password: string; +}; + +export type ResponseSigninDto = CommonResponse<{ + id: number; + name: string; + accessToken: string; + refreshToken: string; +}>; + +export type ResponseMyInfoDto = CommonResponse<{ + id: number; + name: string; + email: string; + bio: string | null; + avatar: string | null; + createAt: string; + updateAt: string; +}>; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/common.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/common.ts" new file mode 100644 index 00000000..0a1b1c2d --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/types/common.ts" @@ -0,0 +1,6 @@ +export type CommonResponse = { + status: boolean; + statusCode: number; + message: string; + data: T; +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/schema.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/schema.ts" new file mode 100644 index 00000000..0fd3dfa0 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/schema.ts" @@ -0,0 +1,21 @@ +import { z } from "zod"; + +export const signupSchema = z + .object({ + email: z.string().email("이메일 형식이 올바르지 않습니다."), + password: z + .string() + .min(8, "비밀번호는 8자 이상이어야 합니다.") + .max(20, "비밀번호는 20자 이하여야 합니다."), + passwordConfirm: z.string(), + nickname: z + .string() + .min(2, "닉네임은 2자 이상이어야 합니다.") + .max(10, "닉네임은 10자 이하여야 합니다."), + }) + .refine((data) => data.password === data.passwordConfirm, { + message: "비밀번호가 일치하지 않습니다.", + path: ["passwordConfirm"], + }); + +export type SignupForm = z.infer; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/validate.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/validate.ts" new file mode 100644 index 00000000..bfee1409 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/utils/validate.ts" @@ -0,0 +1,65 @@ +export type UserSigninInformation = { + email: string; + password: string; +}; + +export type SignupForm = { + email: string; + password: string; + passwordConfirm: string; + nickname: string; +}; + +export function validateSignin( + values: UserSigninInformation +): Partial { + const errors: Partial = {}; + + if (!values.email) { + errors.email = "이메일을 입력해주세요."; + } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { + errors.email = "올바른 이메일 형식이 아닙니다."; + } + + if (!values.password) { + errors.password = "비밀번호를 입력해주세요"; + } else if (values.password.length < 8 || values.password.length > 20) { + errors.password = "비밀번호는 8자 이상 20자 이하로 입력해주세요"; + } + + return errors; +} + +export function validateSignupPassword( + values: SignupForm +): Partial { + const errors: Partial = {}; + + if (!values.password) { + errors.password = "비밀번호를 입력해주세요"; + } else if (values.password.length < 8 || values.password.length > 20) { + errors.password = "비밀번호는 8자 이상 20자 이하로 입력해주세요"; + } + + if (!values.passwordConfirm) { + errors.passwordConfirm = "비밀번호를 다시 입력해주세요"; + } else if (values.password !== values.passwordConfirm) { + errors.passwordConfirm = "비밀번호가 일치하지 않습니다"; + } + + return errors; +} + +export function validateSignupNickname( + values: Pick +): Partial> { + const errors: Partial> = {}; + + if (!values.nickname) { + errors.nickname = "닉네임을 입력해주세요"; + } else if (values.nickname.length < 2 || values.nickname.length > 10) { + errors.nickname = "닉네임은 2자 이상 10자 이하로 입력해주세요"; + } + + return errors; +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/vite-env.d.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/vite-env.d.ts" new file mode 100644 index 00000000..fb64ea2b --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/src/vite-env.d.ts" @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_API_URL: string; // 너가 로그인 요청할 백엔드 API 주소 +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tailwind.config.js" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tailwind.config.js" new file mode 100644 index 00000000..614c86b4 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tailwind.config.js" @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.app.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.app.json" new file mode 100644 index 00000000..5645a1f8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.app.json" @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "node", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* Types */ + "types": ["vite/client", "react/jsx-runtime"] + }, + "include": ["src"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.json" new file mode 100644 index 00000000..f2574791 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.json" @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "types": ["vite/client"] + } +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.node.json" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.node.json" new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/tsconfig.node.json" @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git "a/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/vite.config.ts" "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/vite.config.ts" new file mode 100644 index 00000000..1d8fa731 --- /dev/null +++ "b/\353\252\250\354\271\264_\354\236\245\355\235\254\354\240\225/week4-mission2/vite.config.ts" @@ -0,0 +1,7 @@ +// vite.config.ts +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], +});