refacto: tentative de refacto

This commit is contained in:
Julien Froidefond
2025-02-17 16:37:48 +01:00
parent 7ee99ac31a
commit ba725bb1a3
28 changed files with 195 additions and 170 deletions

View File

@@ -1,11 +1,38 @@
{ {
"extends": ["next/core-web-vitals", "prettier"], "extends": ["next/core-web-vitals", "prettier"],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"], "plugins": ["@typescript-eslint", "unused-imports"],
"rules": { "rules": {
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": [
"no-console": "off", "warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
],
"no-console": [
"warn",
{
"allow": ["warn", "error"]
}
],
"@next/next/no-html-link-for-pages": "off", "@next/next/no-html-link-for-pages": "off",
"react/no-unescaped-entities": "off" "react/no-unescaped-entities": "off",
"no-unreachable": "error",
"no-unused-expressions": "warn",
"no-unused-private-class-members": "warn",
"unused-imports/no-unused-imports": "warn",
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"no-empty-function": "warn",
"no-empty": ["warn", { "allowEmptyCatch": true }]
} }
} }

View File

@@ -35,12 +35,13 @@
"@types/node": "20.11.16", "@types/node": "20.11.16",
"@types/react": "18.2.52", "@types/react": "18.2.52",
"@types/react-dom": "18.2.18", "@types/react-dom": "18.2.18",
"@typescript-eslint/eslint-plugin": "6.21.0", "@typescript-eslint/eslint-plugin": "^8.24.0",
"@typescript-eslint/parser": "6.21.0", "@typescript-eslint/parser": "6.21.0",
"autoprefixer": "10.4.17", "autoprefixer": "10.4.17",
"eslint": "8.56.0", "eslint": "8.56.0",
"eslint-config-next": "14.1.0", "eslint-config-next": "14.1.0",
"eslint-config-prettier": "10.0.1", "eslint-config-prettier": "10.0.1",
"eslint-plugin-unused-imports": "^4.1.4",
"postcss": "8.4.33", "postcss": "8.4.33",
"tailwindcss": "3.4.1", "tailwindcss": "3.4.1",
"typescript": "5.3.3" "typescript": "5.3.3"

View File

@@ -1,6 +1,8 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { ConfigDBService } from "@/lib/services/config-db.service"; import { ConfigDBService } from "@/lib/services/config-db.service";
export const dynamic = "force-dynamic";
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
const data = await request.json(); const data = await request.json();

View File

@@ -10,7 +10,7 @@ export async function GET(
try { try {
// Convertir le numéro de page en nombre // Convertir le numéro de page en nombre
const pageNumber = parseInt(params.pageNumber); const pageNumber = parseInt(params.pageNumber);
if (isNaN(pageNumber) || pageNumber < 1) { if (isNaN(pageNumber) || pageNumber < 0) {
return NextResponse.json({ error: "Numéro de page invalide" }, { status: 400 }); return NextResponse.json({ error: "Numéro de page invalide" }, { status: 400 });
} }
@@ -18,8 +18,11 @@ export async function GET(
return response; return response;
} catch (error) { } catch (error) {
console.error("API Book Page Thumbnail - Erreur:", error); console.error("API Book Page Thumbnail - Erreur:", error);
if (error instanceof Error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
return NextResponse.json( return NextResponse.json(
{ error: "Erreur lors de la récupération de la miniature" }, { error: "Une erreur est survenue lors de la récupération de la miniature" },
{ status: 500 } { status: 500 }
); );
} }

View File

@@ -1,5 +1,15 @@
import { ConfigDBService } from "@/lib/services/config-db.service"; import { ConfigDBService } from "@/lib/services/config-db.service";
import { ClientSettings } from "@/components/settings/ClientSettings"; import { ClientSettings } from "@/components/settings/ClientSettings";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Préférences",
description: "Configurez vos préférences StripStream",
};
export const viewport = {
colorScheme: "dark light",
};
export default async function SettingsPage() { export default async function SettingsPage() {
let config = null; let config = null;

View File

@@ -23,10 +23,6 @@ export function HomeContent({ data }: HomeContentProps) {
await router.push(path); await router.push(path);
}; };
const handleSeriesClick = (seriesId: string) => {
router.push(`/series/${seriesId}`);
};
// Vérification des données pour le debug // Vérification des données pour le debug
// console.log("HomeContent - Données reçues:", { // console.log("HomeContent - Données reçues:", {
// ongoingCount: data.ongoing?.length || 0, // ongoingCount: data.ongoing?.length || 0,

View File

@@ -86,18 +86,9 @@ function MediaCard({ item, onClick }: MediaCardProps) {
? item.metadata.title ? item.metadata.title
: item.metadata.title || `Tome ${item.metadata.number}`; : item.metadata.title || `Tome ${item.metadata.number}`;
const handleClick = () => {
console.log("MediaCard - handleClick:", {
itemType: isSeries ? "series" : "book",
itemId: item.id,
itemTitle: title,
});
onClick?.();
};
return ( return (
<div <div
onClick={handleClick} onClick={onClick}
className="flex-shrink-0 w-[200px] relative flex flex-col rounded-lg border bg-card text-card-foreground shadow-sm hover:bg-accent hover:text-accent-foreground transition-colors overflow-hidden cursor-pointer" className="flex-shrink-0 w-[200px] relative flex flex-col rounded-lg border bg-card text-card-foreground shadow-sm hover:bg-accent hover:text-accent-foreground transition-colors overflow-hidden cursor-pointer"
> >
{/* Image de couverture */} {/* Image de couverture */}

View File

@@ -6,8 +6,7 @@ import { Header } from "@/components/layout/Header";
import { Sidebar } from "@/components/layout/Sidebar"; import { Sidebar } from "@/components/layout/Sidebar";
import { InstallPWA } from "../ui/InstallPWA"; import { InstallPWA } from "../ui/InstallPWA";
import { Toaster } from "@/components/ui/toaster"; import { Toaster } from "@/components/ui/toaster";
import { usePathname, useRouter } from "next/navigation"; import { usePathname } from "next/navigation";
import { authService } from "@/lib/services/auth.service";
import { PreferencesProvider } from "@/contexts/PreferencesContext"; import { PreferencesProvider } from "@/contexts/PreferencesContext";
// Routes qui ne nécessitent pas d'authentification // Routes qui ne nécessitent pas d'authentification
@@ -15,7 +14,6 @@ const publicRoutes = ["/login", "/register"];
export default function ClientLayout({ children }: { children: React.ReactNode }) { export default function ClientLayout({ children }: { children: React.ReactNode }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const handleCloseSidebar = () => { const handleCloseSidebar = () => {
@@ -56,8 +54,8 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker navigator.serviceWorker
.register("/sw.js") .register("/sw.js")
.then((registration) => { .then(() => {
console.log("Service Worker enregistré avec succès:", registration); // Succès silencieux
}) })
.catch((error) => { .catch((error) => {
console.error("Erreur lors de l'enregistrement du Service Worker:", error); console.error("Erreur lors de l'enregistrement du Service Worker:", error);

View File

@@ -1,7 +1,6 @@
"use client"; "use client";
import { BookOpen, Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react"; import { Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { authService } from "@/lib/services/auth.service"; import { authService } from "@/lib/services/auth.service";
@@ -32,7 +31,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const data = await response.json(); const data = await response.json();
setLibraries(data); setLibraries(data);
} catch (error) { } catch (error) {
console.error("Erreur:", error); if (error instanceof Error) {
console.error("Erreur de chargement des bibliothèques:", error.message);
}
setLibraries([]); setLibraries([]);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -43,7 +44,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const fetchFavorites = useCallback(async () => { const fetchFavorites = useCallback(async () => {
setIsLoadingFavorites(true); setIsLoadingFavorites(true);
try { try {
// Récupérer les IDs des favoris depuis l'API
const favoritesResponse = await fetch("/api/komga/favorites"); const favoritesResponse = await fetch("/api/komga/favorites");
if (!favoritesResponse.ok) { if (!favoritesResponse.ok) {
throw new Error("Erreur lors de la récupération des favoris"); throw new Error("Erreur lors de la récupération des favoris");
@@ -55,7 +55,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
return; return;
} }
// Récupérer les détails des séries pour chaque ID
const promises = favoriteIds.map(async (id: string) => { const promises = favoriteIds.map(async (id: string) => {
const response = await fetch(`/api/komga/series/${id}`); const response = await fetch(`/api/komga/series/${id}`);
if (!response.ok) return null; if (!response.ok) return null;
@@ -65,7 +64,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const results = await Promise.all(promises); const results = await Promise.all(promises);
setFavorites(results.filter((series): series is KomgaSeries => series !== null)); setFavorites(results.filter((series): series is KomgaSeries => series !== null));
} catch (error) { } catch (error) {
console.error("Erreur lors de la récupération des favoris:", error); if (error instanceof Error) {
console.error("Erreur de chargement des favoris:", error.message);
}
setFavorites([]); setFavorites([]);
} finally { } finally {
setIsLoadingFavorites(false); setIsLoadingFavorites(false);

View File

@@ -6,9 +6,10 @@ import { useRouter, usePathname, useSearchParams } from "next/navigation";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Loader2, Filter } from "lucide-react"; import { Loader2, Filter } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { KomgaSeries } from "@/types/komga";
interface PaginatedSeriesGridProps { interface PaginatedSeriesGridProps {
series: any[]; series: KomgaSeries[];
currentPage: number; currentPage: number;
totalPages: number; totalPages: number;
totalElements: number; totalElements: number;
@@ -61,10 +62,6 @@ export function PaginatedSeriesGrid({
await router.push(`${pathname}?${params.toString()}`); await router.push(`${pathname}?${params.toString()}`);
}; };
const handleSeriesClick = (seriesId: string) => {
router.push(`/series/${seriesId}`);
};
// Calcul des indices de début et de fin pour l'affichage // Calcul des indices de début et de fin pour l'affichage
const startIndex = (currentPage - 1) * pageSize + 1; const startIndex = (currentPage - 1) * pageSize + 1;
const endIndex = Math.min(currentPage * pageSize, totalElements); const endIndex = Math.min(currentPage * pageSize, totalElements);

View File

@@ -24,12 +24,9 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
setIsLoading, setIsLoading,
secondPageLoading, secondPageLoading,
setSecondPageLoading, setSecondPageLoading,
imageError,
setImageError,
handlePreviousPage, handlePreviousPage,
handleNextPage, handleNextPage,
shouldShowDoublePage, shouldShowDoublePage,
syncReadProgress,
} = usePageNavigation({ } = usePageNavigation({
book, book,
pages, pages,
@@ -66,9 +63,12 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
} }
} }
} catch (error) { } catch (error) {
console.error("Erreur lors du chargement des URLs:", error); if (error instanceof Error) {
setImageError(true); console.error(
} finally { `Erreur de chargement des URLs pour la page ${currentPage}:`,
error.message
);
}
// On s'assure que le chargement est terminé même en cas d'erreur // On s'assure que le chargement est terminé même en cas d'erreur
if (isMounted) { if (isMounted) {
setIsLoading(false); setIsLoading(false);
@@ -91,7 +91,6 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
getPageUrl, getPageUrl,
setIsLoading, setIsLoading,
setSecondPageLoading, setSecondPageLoading,
setImageError,
]); ]);
// Effet pour précharger la page courante et les pages adjacentes // Effet pour précharger la page courante et les pages adjacentes
@@ -205,6 +204,13 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
{/* Pages */} {/* Pages */}
<div className="relative flex-1 flex items-center justify-center overflow-hidden p-1"> <div className="relative flex-1 flex items-center justify-center overflow-hidden p-1">
<div className="relative w-full h-[calc(100vh-2rem)] flex items-center justify-center gap-0"> <div className="relative w-full h-[calc(100vh-2rem)] flex items-center justify-center gap-0">
{/*
Note: Nous utilisons intentionnellement des balises <img> natives au lieu de next/image pour :
1. Avoir un contrôle précis sur le chargement et le préchargement des pages
2. Gérer efficacement le mode double page et les transitions
3. Les images sont déjà optimisées côté serveur
4. La performance est critique pour une lecture fluide
*/}
{/* Page courante */} {/* Page courante */}
<div <div
className={cn( className={cn(

View File

@@ -3,8 +3,6 @@
import { KomgaBook } from "@/types/komga"; import { KomgaBook } from "@/types/komga";
import { BookReader } from "./BookReader"; import { BookReader } from "./BookReader";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useToast } from "@/components/ui/use-toast";
interface ClientBookWrapperProps { interface ClientBookWrapperProps {
book: KomgaBook; book: KomgaBook;

View File

@@ -31,6 +31,10 @@ export const ControlButtons = ({
"absolute top-4 left-1/2 -translate-x-1/2 z-30 flex items-center gap-2 transition-all duration-300", "absolute top-4 left-1/2 -translate-x-1/2 z-30 flex items-center gap-2 transition-all duration-300",
showControls ? "opacity-100" : "opacity-0 pointer-events-none" showControls ? "opacity-100" : "opacity-0 pointer-events-none"
)} )}
onClick={(e) => {
e.stopPropagation();
onToggleControls();
}}
> >
<button <button
onClick={(e) => { onClick={(e) => {

View File

@@ -2,7 +2,7 @@ import { NavigationBarProps } from "../types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Thumbnail } from "./Thumbnail"; import { Thumbnail } from "./Thumbnail";
import { useThumbnails } from "../hooks/useThumbnails"; import { useThumbnails } from "../hooks/useThumbnails";
import { useEffect } from "react"; import { useEffect, useRef } from "react";
export const NavigationBar = ({ export const NavigationBar = ({
currentPage, currentPage,
@@ -11,16 +11,13 @@ export const NavigationBar = ({
showControls, showControls,
book, book,
}: NavigationBarProps) => { }: NavigationBarProps) => {
const { const { loadedThumbnails, handleThumbnailLoad, getThumbnailUrl, visibleThumbnails } =
loadedThumbnails, useThumbnails({
handleThumbnailLoad, book,
getThumbnailUrl, currentPage,
visibleThumbnails, });
scrollToActiveThumbnail,
} = useThumbnails({ const thumbnailsContainerRef = useRef<HTMLDivElement>(null);
book,
currentPage,
});
// Scroll à l'ouverture des contrôles et au changement de page // Scroll à l'ouverture des contrôles et au changement de page
useEffect(() => { useEffect(() => {
@@ -53,6 +50,7 @@ export const NavigationBar = ({
onTouchStart={(e) => e.stopPropagation()} onTouchStart={(e) => e.stopPropagation()}
onTouchMove={(e) => e.stopPropagation()} onTouchMove={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()} onTouchEnd={(e) => e.stopPropagation()}
ref={thumbnailsContainerRef}
> >
<div className="w-[calc(50vw-18rem)] flex-shrink-0" /> <div className="w-[calc(50vw-18rem)] flex-shrink-0" />
{pages.map((_, index) => { {pages.map((_, index) => {

View File

@@ -6,15 +6,7 @@ import { forwardRef, useEffect, useState, useCallback, useRef } from "react";
export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>( export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
( (
{ { pageNumber, currentPage, onPageChange, getThumbnailUrl, loadedThumbnails, onThumbnailLoad },
pageNumber,
currentPage,
onPageChange,
getThumbnailUrl,
loadedThumbnails,
onThumbnailLoad,
isVisible,
},
ref ref
) => { ) => {
const [imageUrl, setImageUrl] = useState<string | null>(null); const [imageUrl, setImageUrl] = useState<string | null>(null);
@@ -59,9 +51,6 @@ export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
if (loadAttempts.current < maxAttempts) { if (loadAttempts.current < maxAttempts) {
// Réessayer avec un délai croissant // Réessayer avec un délai croissant
const delay = Math.min(1000 * Math.pow(2, loadAttempts.current - 1), 5000); const delay = Math.min(1000 * Math.pow(2, loadAttempts.current - 1), 5000);
console.log(
`Réessai ${loadAttempts.current}/${maxAttempts} dans ${delay}ms pour la page ${pageNumber}`
);
setTimeout(() => { setTimeout(() => {
setImageUrl((prev) => (prev ? `${prev}?retry=${loadAttempts.current}` : null)); setImageUrl((prev) => (prev ? `${prev}?retry=${loadAttempts.current}` : null));
}, delay); }, delay);
@@ -94,7 +83,7 @@ export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
src={imageUrl} src={imageUrl}
alt={`Miniature page ${pageNumber}`} alt={`Miniature page ${pageNumber}`}
className={cn( className={cn(
"object-cover transition-opacity duration-300", "object-contain transition-opacity duration-300",
isLoading ? "opacity-0" : "opacity-100" isLoading ? "opacity-0" : "opacity-100"
)} )}
fill fill

View File

@@ -12,12 +12,11 @@ export const usePageNavigation = ({
book, book,
pages, pages,
isDoublePage, isDoublePage,
onClose = () => {}, onClose,
}: UsePageNavigationProps) => { }: UsePageNavigationProps) => {
const [currentPage, setCurrentPage] = useState(book.readProgress?.page || 1); const [currentPage, setCurrentPage] = useState(book.readProgress?.page || 1);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [secondPageLoading, setSecondPageLoading] = useState(true); const [secondPageLoading, setSecondPageLoading] = useState(true);
const [imageError, setImageError] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null); const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const touchStartXRef = useRef<number | null>(null); const touchStartXRef = useRef<number | null>(null);
const touchStartYRef = useRef<number | null>(null); const touchStartYRef = useRef<number | null>(null);
@@ -39,7 +38,12 @@ export const usePageNavigation = ({
body: JSON.stringify({ page, completed }), body: JSON.stringify({ page, completed }),
}); });
} catch (error) { } catch (error) {
console.error("Erreur lors de la synchronisation de la progression:", error); if (error instanceof Error) {
console.error(
`Erreur de synchronisation de la progression pour le livre ${book.id} à la page ${page}:`,
error.message
);
}
} }
}, },
[book.id, pages.length] [book.id, pages.length]
@@ -73,7 +77,6 @@ export const usePageNavigation = ({
setCurrentPage(page); setCurrentPage(page);
setIsLoading(true); setIsLoading(true);
setSecondPageLoading(true); setSecondPageLoading(true);
setImageError(false);
debouncedSyncReadProgress(page); debouncedSyncReadProgress(page);
}, },
[debouncedSyncReadProgress] [debouncedSyncReadProgress]
@@ -167,11 +170,8 @@ export const usePageNavigation = ({
setIsLoading, setIsLoading,
secondPageLoading, secondPageLoading,
setSecondPageLoading, setSecondPageLoading,
imageError,
setImageError,
handlePreviousPage, handlePreviousPage,
handleNextPage, handleNextPage,
shouldShowDoublePage, shouldShowDoublePage,
syncReadProgress: debouncedSyncReadProgress,
}; };
}; };

View File

@@ -16,7 +16,8 @@ export const useThumbnails = ({ book, currentPage }: UseThumbnailsProps) => {
const getThumbnailUrl = useCallback( const getThumbnailUrl = useCallback(
(pageNumber: number) => { (pageNumber: number) => {
return `/api/komga/images/books/${book.id}/pages/${pageNumber}/thumbnail`; const zeroBasedPage = pageNumber - 1;
return `/api/komga/images/books/${book.id}/pages/${zeroBasedPage}/thumbnail?zero_based=true`;
}, },
[book.id] [book.id]
); );

View File

@@ -70,10 +70,6 @@ export function PaginatedBookGrid({
const startIndex = (currentPage - 1) * pageSize + 1; const startIndex = (currentPage - 1) * pageSize + 1;
const endIndex = Math.min(currentPage * pageSize, totalElements); const endIndex = Math.min(currentPage * pageSize, totalElements);
const getBookThumbnailUrl = (bookId: string) => {
return `/api/komga/images/books/${bookId}/thumbnail`;
};
return ( return (
<div className="space-y-8"> <div className="space-y-8">
<div className="flex items-center justify-between flex-wrap gap-4"> <div className="flex items-center justify-between flex-wrap gap-4">
@@ -115,11 +111,7 @@ export function PaginatedBookGrid({
isChangingPage ? "opacity-25" : "opacity-100" isChangingPage ? "opacity-25" : "opacity-100"
)} )}
> >
<BookGrid <BookGrid books={books} onBookClick={handleBookClick} />
books={books}
onBookClick={handleBookClick}
getBookThumbnailUrl={getBookThumbnailUrl}
/>
</div> </div>
</div> </div>

View File

@@ -5,15 +5,13 @@ import { KomgaSeries } from "@/types/komga";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { cn } from "@/lib/utils";
import { Cover } from "@/components/ui/cover"; import { Cover } from "@/components/ui/cover";
interface SeriesHeaderProps { interface SeriesHeaderProps {
series: KomgaSeries; series: KomgaSeries;
onSeriesUpdate?: (series: KomgaSeries) => void;
} }
export const SeriesHeader = ({ series, onSeriesUpdate }: SeriesHeaderProps) => { export const SeriesHeader = ({ series }: SeriesHeaderProps) => {
const { toast } = useToast(); const { toast } = useToast();
const [isFavorite, setIsFavorite] = useState(false); const [isFavorite, setIsFavorite] = useState(false);

View File

@@ -9,10 +9,6 @@ import { usePreferences } from "@/contexts/PreferencesContext";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
interface ErrorMessage {
message: string;
}
interface KomgaConfig { interface KomgaConfig {
url: string; url: string;
username: string; username: string;
@@ -256,7 +252,10 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
toast({ toast({
variant: "destructive", variant: "destructive",
title: "Erreur", title: "Erreur",
description: "Une erreur est survenue lors de la mise à jour des préférences", description:
error instanceof Error
? error.message
: "Une erreur est survenue lors de la mise à jour des préférences",
}); });
} }
}; };

View File

@@ -1,6 +1,5 @@
"use client"; "use client";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
interface ImageLoaderProps { interface ImageLoaderProps {

View File

@@ -12,7 +12,7 @@ type ToasterToast = ToastProps & {
action?: ToastActionElement; action?: ToastActionElement;
}; };
const actionTypes = { const _actionTypes = {
ADD_TOAST: "ADD_TOAST", ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST", UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST", DISMISS_TOAST: "DISMISS_TOAST",
@@ -26,7 +26,7 @@ function genId() {
return count.toString(); return count.toString();
} }
type ActionType = typeof actionTypes; type ActionType = typeof _actionTypes;
type Action = type Action =
| { | {

View File

@@ -2,16 +2,13 @@
import { AuthError } from "@/types/auth"; import { AuthError } from "@/types/auth";
interface AuthUser {
id: string;
email: string;
roles: string[];
authenticated: boolean;
}
class AuthService { class AuthService {
private static instance: AuthService; private static instance: AuthService;
private constructor() {} // Constructeur privé pour le pattern Singleton
private constructor() {
// Pas d'initialisation nécessaire
}
public static getInstance(): AuthService { public static getInstance(): AuthService {
if (!AuthService.instance) { if (!AuthService.instance) {

View File

@@ -1,4 +1,3 @@
import { cookies } from "next/headers";
import { AuthConfig } from "@/types/auth"; import { AuthConfig } from "@/types/auth";
import { serverCacheService } from "./server-cache.service"; import { serverCacheService } from "./server-cache.service";
import { ConfigDBService } from "./config-db.service"; import { ConfigDBService } from "./config-db.service";
@@ -71,18 +70,12 @@ export abstract class BaseApiService {
}); });
} }
// Log de l'URL finale
console.log(`🔄 [Komga API] ${url.toString()}`);
return url.toString(); return url.toString();
} }
protected static async fetchFromApi<T>(url: string, headers: Headers): Promise<T> { protected static async fetchFromApi<T>(url: string, headers: Headers): Promise<T> {
const response = await fetch(url, { headers }); const response = await fetch(url, { headers });
// Log du résultat de la requête
console.log(`📡 [Komga API] ${response.status} ${response.statusText} - ${url}`);
if (!response.ok) { if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`); throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`);
} }

View File

@@ -18,9 +18,6 @@ export class ImageService extends BaseApiService {
async () => { async () => {
const response = await fetch(url, { headers }); const response = await fetch(url, { headers });
// Log du résultat de la requête
console.log(`📡 [Komga API] ${response.status} ${response.statusText} - ${url}`);
if (!response.ok) { if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`); throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`);
} }

View File

@@ -1,9 +1,3 @@
type CacheEntry = {
data: any;
timestamp: number;
ttl: number;
};
class ServerCacheService { class ServerCacheService {
private static instance: ServerCacheService; private static instance: ServerCacheService;
private cache: Map<string, { data: unknown; expiry: number }> = new Map(); private cache: Map<string, { data: unknown; expiry: number }> = new Map();
@@ -94,14 +88,11 @@ class ServerCacheService {
type: keyof typeof ServerCacheService.DEFAULT_TTL = "DEFAULT" type: keyof typeof ServerCacheService.DEFAULT_TTL = "DEFAULT"
): Promise<T> { ): Promise<T> {
const now = Date.now(); const now = Date.now();
console.log("👀 Getting or setting cache for key:", key);
const cached = this.cache.get(key); const cached = this.cache.get(key);
if (cached && cached.expiry > now) { if (cached && cached.expiry > now) {
console.log("✅ Cache hit for key:", key);
return cached.data as T; return cached.data as T;
} }
console.log("❌ Cache not hit for key:", key);
try { try {
const data = await fetcher(); const data = await fetcher();

View File

@@ -11,13 +11,13 @@ export interface Series {
booksUnreadCount: number; booksUnreadCount: number;
booksInProgressCount: number; booksInProgressCount: number;
metadata: { metadata: {
status: string; status: "ENDED" | "ONGOING" | "ABANDONED" | "HIATUS";
created: string; created: string;
lastModified: string; lastModified: string;
title: string; title: string;
titleSort: string; titleSort: string;
summary: string; summary: string;
readingDirection: string; readingDirection: "LEFT_TO_RIGHT" | "RIGHT_TO_LEFT" | "VERTICAL" | "WEBTOON";
publisher: string; publisher: string;
ageRating: number; ageRating: number;
language: string; language: string;
@@ -47,4 +47,6 @@ export interface Series {
}>; }>;
}; };
deleted: boolean; deleted: boolean;
oneshot: boolean;
favorite: boolean;
} }

123
yarn.lock
View File

@@ -28,7 +28,7 @@
dependencies: dependencies:
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
version "4.12.1" version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
@@ -741,11 +741,6 @@
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
"@types/json-schema@^7.0.12":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/json5@^0.0.29": "@types/json5@^0.0.29":
version "0.0.29" version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -798,11 +793,6 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09"
integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw== integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==
"@types/semver@^7.5.0":
version "7.5.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
"@types/webidl-conversions@*": "@types/webidl-conversions@*":
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859" resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859"
@@ -815,22 +805,20 @@
dependencies: dependencies:
"@types/webidl-conversions" "*" "@types/webidl-conversions" "*"
"@typescript-eslint/eslint-plugin@6.21.0": "@typescript-eslint/eslint-plugin@^8.24.0":
version "6.21.0" version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz#574a95d67660a1e4544ae131d672867a5b40abb3"
integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== integrity sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.5.1" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "6.21.0" "@typescript-eslint/scope-manager" "8.24.0"
"@typescript-eslint/type-utils" "6.21.0" "@typescript-eslint/type-utils" "8.24.0"
"@typescript-eslint/utils" "6.21.0" "@typescript-eslint/utils" "8.24.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "8.24.0"
debug "^4.3.4"
graphemer "^1.4.0" graphemer "^1.4.0"
ignore "^5.2.4" ignore "^5.3.1"
natural-compare "^1.4.0" natural-compare "^1.4.0"
semver "^7.5.4" ts-api-utils "^2.0.1"
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@6.21.0", "@typescript-eslint/parser@^5.4.2 || ^6.0.0": "@typescript-eslint/parser@6.21.0", "@typescript-eslint/parser@^5.4.2 || ^6.0.0":
version "6.21.0" version "6.21.0"
@@ -851,21 +839,34 @@
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0"
"@typescript-eslint/type-utils@6.21.0": "@typescript-eslint/scope-manager@8.24.0":
version "6.21.0" version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz#2e34b3eb2ce768f2ffb109474174ced5417002b1"
integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== integrity sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "6.21.0" "@typescript-eslint/types" "8.24.0"
"@typescript-eslint/utils" "6.21.0" "@typescript-eslint/visitor-keys" "8.24.0"
"@typescript-eslint/type-utils@8.24.0":
version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz#6ee3ec4db06f9e5e7b01ca6c2b5dd5843a9fd1e8"
integrity sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==
dependencies:
"@typescript-eslint/typescript-estree" "8.24.0"
"@typescript-eslint/utils" "8.24.0"
debug "^4.3.4" debug "^4.3.4"
ts-api-utils "^1.0.1" ts-api-utils "^2.0.1"
"@typescript-eslint/types@6.21.0": "@typescript-eslint/types@6.21.0":
version "6.21.0" version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
"@typescript-eslint/types@8.24.0":
version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.24.0.tgz#694e7fb18d70506c317b816de9521300b0f72c8e"
integrity sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==
"@typescript-eslint/typescript-estree@6.21.0": "@typescript-eslint/typescript-estree@6.21.0":
version "6.21.0" version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
@@ -880,18 +881,29 @@
semver "^7.5.4" semver "^7.5.4"
ts-api-utils "^1.0.1" ts-api-utils "^1.0.1"
"@typescript-eslint/utils@6.21.0": "@typescript-eslint/typescript-estree@8.24.0":
version "6.21.0" version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz#0487349be174097bb329a58273100a9629e03c6c"
integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== integrity sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==
dependencies:
"@typescript-eslint/types" "8.24.0"
"@typescript-eslint/visitor-keys" "8.24.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.24.0":
version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.24.0.tgz#21cb1195ae79230af825bfeed59574f5cb70a749"
integrity sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12" "@typescript-eslint/scope-manager" "8.24.0"
"@types/semver" "^7.5.0" "@typescript-eslint/types" "8.24.0"
"@typescript-eslint/scope-manager" "6.21.0" "@typescript-eslint/typescript-estree" "8.24.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
semver "^7.5.4"
"@typescript-eslint/visitor-keys@6.21.0": "@typescript-eslint/visitor-keys@6.21.0":
version "6.21.0" version "6.21.0"
@@ -901,6 +913,14 @@
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "6.21.0"
eslint-visitor-keys "^3.4.1" eslint-visitor-keys "^3.4.1"
"@typescript-eslint/visitor-keys@8.24.0":
version "8.24.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz#36ecf0b9b1d819ad88a3bd4157ab7d594cb797c9"
integrity sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==
dependencies:
"@typescript-eslint/types" "8.24.0"
eslint-visitor-keys "^4.2.0"
"@ungap/structured-clone@^1.2.0": "@ungap/structured-clone@^1.2.0":
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
@@ -1720,6 +1740,11 @@ eslint-plugin-react@^7.33.2:
string.prototype.matchall "^4.0.12" string.prototype.matchall "^4.0.12"
string.prototype.repeat "^1.0.0" string.prototype.repeat "^1.0.0"
eslint-plugin-unused-imports@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738"
integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==
eslint-scope@^7.2.2: eslint-scope@^7.2.2:
version "7.2.2" version "7.2.2"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
@@ -1733,6 +1758,11 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint-visitor-keys@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
eslint@8.56.0: eslint@8.56.0:
version "8.56.0" version "8.56.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15"
@@ -1815,7 +1845,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9, fast-glob@^3.3.0: fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
@@ -2115,7 +2145,7 @@ hasown@^2.0.2:
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
ignore@^5.2.0, ignore@^5.2.4: ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
@@ -3246,7 +3276,7 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.5.4, semver@^7.6.3: semver@^7.5.4, semver@^7.6.0, semver@^7.6.3:
version "7.7.1" version "7.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
@@ -3661,6 +3691,11 @@ ts-api-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
ts-api-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd"
integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==
ts-interface-checker@^0.1.9: ts-interface-checker@^0.1.9:
version "0.1.13" version "0.1.13"
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"