mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-16 17:23:43 +08:00
refactor: remove debug console logs and the unused latency emoji utility
This commit is contained in:
@@ -28,7 +28,7 @@ async function handleDetailRequest(id: string | null, source: string | null, met
|
||||
}
|
||||
|
||||
const sourceConfig = getSourceById(source);
|
||||
|
||||
|
||||
if (!sourceConfig) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid source ID' },
|
||||
@@ -39,18 +39,18 @@ async function handleDetailRequest(id: string | null, source: string | null, met
|
||||
// Fetch video detail without validation (already validated during search)
|
||||
try {
|
||||
const videoDetail = await getVideoDetail(id, sourceConfig);
|
||||
|
||||
|
||||
// Skip validation - videos are already checked during search
|
||||
// Just return the episodes as-is
|
||||
console.log(`[${method}] Fetching video details for ${id} from ${sourceConfig.name}`);
|
||||
|
||||
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: videoDetail,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Detail API error:', error);
|
||||
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
@@ -70,7 +70,7 @@ export async function GET(request: NextRequest) {
|
||||
return await handleDetailRequest(id, source, 'GET');
|
||||
} catch (error) {
|
||||
console.error('Detail API error:', error);
|
||||
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
@@ -90,7 +90,7 @@ export async function POST(request: NextRequest) {
|
||||
return await handleDetailRequest(id, source, 'POST');
|
||||
} catch (error) {
|
||||
console.error('Detail API error:', error);
|
||||
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getSourceById } from '@/lib/api/video-sources';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
try {
|
||||
@@ -19,9 +19,9 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// Validate input
|
||||
if (!query || typeof query !== 'string' || query.trim().length === 0) {
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'error',
|
||||
message: 'Invalid query'
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'error',
|
||||
message: 'Invalid query'
|
||||
})}\n\n`));
|
||||
controller.close();
|
||||
return;
|
||||
@@ -33,21 +33,21 @@ export async function POST(request: NextRequest) {
|
||||
.filter((source: any): source is NonNullable<typeof source> => source !== undefined);
|
||||
|
||||
if (sources.length === 0) {
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'error',
|
||||
message: 'No valid sources'
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'error',
|
||||
message: 'No valid sources'
|
||||
})}\n\n`));
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send initial status
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'start',
|
||||
totalSources: sources.length
|
||||
})}\n\n`));
|
||||
|
||||
console.log(`[Search Parallel] Starting search for "${query}" across ${sources.length} sources`);
|
||||
|
||||
|
||||
// Track progress
|
||||
let completedSources = 0;
|
||||
@@ -57,22 +57,22 @@ export async function POST(request: NextRequest) {
|
||||
const searchPromises = sources.map(async (source: any) => {
|
||||
const startTime = performance.now(); // Track start time
|
||||
try {
|
||||
console.log(`[Search Parallel] Searching source: ${source.id} (${getSourceDisplayName(source.id)})`);
|
||||
|
||||
|
||||
|
||||
// Search this source
|
||||
const result = await searchVideos(query.trim(), [source], page);
|
||||
const endTime = performance.now(); // Track end time
|
||||
const latency = Math.round(endTime - startTime); // Calculate latency in ms
|
||||
const videos = result[0]?.results || [];
|
||||
|
||||
|
||||
completedSources++;
|
||||
totalVideosFound += videos.length;
|
||||
|
||||
console.log(`[Search Parallel] Source ${source.id} completed in ${latency}ms: ${videos.length} videos found`);
|
||||
|
||||
|
||||
// Stream videos immediately as they arrive WITH latency data
|
||||
if (videos.length > 0) {
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'videos',
|
||||
videos: videos.map((video: any) => ({
|
||||
...video,
|
||||
@@ -87,7 +87,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Send progress update
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'progress',
|
||||
completedSources,
|
||||
totalSources: sources.length,
|
||||
@@ -100,8 +100,8 @@ export async function POST(request: NextRequest) {
|
||||
// Log error but continue with other sources
|
||||
console.error(`[Search Parallel] Source ${source.id} failed after ${latency}ms:`, error);
|
||||
completedSources++;
|
||||
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'progress',
|
||||
completedSources,
|
||||
totalSources: sources.length,
|
||||
@@ -113,10 +113,10 @@ export async function POST(request: NextRequest) {
|
||||
// Wait for all sources to complete
|
||||
await Promise.all(searchPromises);
|
||||
|
||||
console.log(`[Search Parallel] Search complete: ${totalVideosFound} total videos found from ${completedSources}/${sources.length} sources`);
|
||||
|
||||
|
||||
// Send completion signal
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'complete',
|
||||
totalVideosFound,
|
||||
totalSources: sources.length
|
||||
@@ -126,7 +126,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify({
|
||||
type: 'error',
|
||||
message: error instanceof Error ? error.message : 'Unknown error'
|
||||
})}\n\n`));
|
||||
|
||||
+15
-15
@@ -24,7 +24,7 @@ function HomePage() {
|
||||
const searchParams = useSearchParams();
|
||||
const { loadFromCache, saveToCache } = useSearchCache();
|
||||
const hasLoadedCache = useRef(false);
|
||||
|
||||
|
||||
const [query, setQuery] = useState('');
|
||||
const [hasSearched, setHasSearched] = useState(false);
|
||||
const [currentSortBy, setCurrentSortBy] = useState('default');
|
||||
@@ -75,15 +75,15 @@ function HomePage() {
|
||||
|
||||
const urlQuery = searchParams.get('q');
|
||||
const cached = loadFromCache();
|
||||
|
||||
|
||||
if (urlQuery) {
|
||||
setQuery(urlQuery);
|
||||
if (cached && cached.query === urlQuery && cached.results.length > 0) {
|
||||
console.log('📦 Loading cached results for:', urlQuery, cached.results.length, 'videos');
|
||||
|
||||
setHasSearched(true);
|
||||
loadCachedResults(cached.results, cached.availableSources);
|
||||
} else {
|
||||
console.log('🔍 Auto-searching for URL query:', urlQuery);
|
||||
|
||||
setTimeout(() => handleSearch(urlQuery), 100);
|
||||
}
|
||||
}
|
||||
@@ -114,16 +114,16 @@ function HomePage() {
|
||||
transform: 'translate3d(0, 0, 0)'
|
||||
}}>
|
||||
<div className="flex items-center justify-between">
|
||||
<Link
|
||||
href="/"
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center gap-3 hover:opacity-80 transition-opacity cursor-pointer"
|
||||
onClick={handleReset}
|
||||
>
|
||||
<div className="w-10 h-10 relative flex items-center justify-center">
|
||||
<Image
|
||||
src="/icon.png"
|
||||
alt="KVideo"
|
||||
width={40}
|
||||
<Image
|
||||
src="/icon.png"
|
||||
alt="KVideo"
|
||||
width={40}
|
||||
height={40}
|
||||
className="object-contain"
|
||||
/>
|
||||
@@ -133,7 +133,7 @@ function HomePage() {
|
||||
<p className="text-xs text-[var(--text-color-secondary)]">视频聚合平台</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
href="/settings"
|
||||
@@ -141,7 +141,7 @@ function HomePage() {
|
||||
aria-label="设置"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/>
|
||||
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ThemeSwitcher />
|
||||
@@ -177,7 +177,7 @@ function HomePage() {
|
||||
resultsCount={results.length}
|
||||
availableSources={availableSources}
|
||||
/>
|
||||
|
||||
|
||||
{/* Source Badges - Clickable video source filtering */}
|
||||
{availableSources.length > 0 && (
|
||||
<SourceBadges
|
||||
@@ -187,7 +187,7 @@ function HomePage() {
|
||||
className="mb-6"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{/* Type Badges - Auto-collected from search results */}
|
||||
{typeBadges.length > 0 && (
|
||||
<TypeBadges
|
||||
@@ -197,7 +197,7 @@ function HomePage() {
|
||||
className="mb-6"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{/* Display filtered videos (both source and type filters applied) */}
|
||||
<VideoGrid videos={finalFilteredVideos} />
|
||||
</div>
|
||||
|
||||
+7
-11
@@ -54,7 +54,7 @@ async function withRetry<T>(
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
|
||||
|
||||
if (i < retries) {
|
||||
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY * (i + 1)));
|
||||
}
|
||||
@@ -73,7 +73,7 @@ async function searchVideosBySource(
|
||||
page: number = 1
|
||||
): Promise<{ results: VideoItem[]; source: string; responseTime: number }> {
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
const url = new URL(`${source.baseUrl}${source.searchPath}`);
|
||||
url.searchParams.set('ac', 'detail');
|
||||
url.searchParams.set('wd', query);
|
||||
@@ -155,7 +155,7 @@ function parseEpisodes(playUrl: string): Episode[] {
|
||||
try {
|
||||
// Format: "Episode1$url1#Episode2$url2#..."
|
||||
const episodes = playUrl.split('#').filter(Boolean);
|
||||
|
||||
|
||||
return episodes.map((episode, index) => {
|
||||
const [name, url] = episode.split('$');
|
||||
return {
|
||||
@@ -200,11 +200,7 @@ export async function getVideoDetail(
|
||||
|
||||
const data: ApiDetailResponse = await response.json();
|
||||
|
||||
console.log(`Video detail fetched from ${source.name}:`, {
|
||||
id,
|
||||
code: data.code,
|
||||
hasData: !!data.list && data.list.length > 0
|
||||
});
|
||||
|
||||
|
||||
if (data.code !== 1 && data.code !== 0) {
|
||||
throw new Error(data.msg || 'Invalid API response');
|
||||
@@ -215,15 +211,15 @@ export async function getVideoDetail(
|
||||
}
|
||||
|
||||
const videoData = data.list[0];
|
||||
|
||||
|
||||
// Parse episodes from vod_play_url
|
||||
const episodes = parseEpisodes(videoData.vod_play_url || '');
|
||||
|
||||
console.log(`Parsed ${episodes.length} episodes for video ${id}`);
|
||||
|
||||
if (episodes.length > 0) {
|
||||
console.log('First episode URL:', episodes[0].url);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
vod_id: videoData.vod_id,
|
||||
vod_name: videoData.vod_name,
|
||||
|
||||
@@ -47,7 +47,7 @@ export function useParallelSearch(
|
||||
const [totalSources, setTotalSources] = useState(0);
|
||||
const [totalVideosFound, setTotalVideosFound] = useState(0);
|
||||
const currentQueryRef = useRef<string>('');
|
||||
|
||||
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
/**
|
||||
@@ -107,8 +107,8 @@ export function useParallelSearch(
|
||||
|
||||
if (data.type === 'start') {
|
||||
setTotalSources(data.totalSources);
|
||||
console.log(`[useParallelSearch] Started search for ${data.totalSources} sources`);
|
||||
}
|
||||
|
||||
}
|
||||
else if (data.type === 'videos') {
|
||||
const currentQuery = currentQueryRef.current;
|
||||
const newVideos: Video[] = data.videos.map((video: any) => ({
|
||||
@@ -118,18 +118,18 @@ export function useParallelSearch(
|
||||
relevanceScore: calculateRelevanceScore(video, currentQuery),
|
||||
}));
|
||||
|
||||
console.log(`[useParallelSearch] Received ${newVideos.length} videos from source ${data.source}`);
|
||||
|
||||
|
||||
// Optimized: Insert new videos in sorted position instead of re-sorting entire array
|
||||
setResults((prev) => {
|
||||
if (prev.length === 0) return newVideos;
|
||||
|
||||
|
||||
// Binary insert for better performance with combined sorting
|
||||
const combined = [...prev];
|
||||
for (const video of newVideos) {
|
||||
const relevanceScore = video.relevanceScore || 0;
|
||||
const latency = video.latency || 99999; // Default high latency for sorting
|
||||
|
||||
|
||||
// Find insert position using binary search
|
||||
// Sort by: 1) relevance score (DESC), 2) latency (ASC)
|
||||
let left = 0;
|
||||
@@ -138,7 +138,7 @@ export function useParallelSearch(
|
||||
const mid = Math.floor((left + right) / 2);
|
||||
const midRelevance = combined[mid].relevanceScore || 0;
|
||||
const midLatency = combined[mid].latency || 99999;
|
||||
|
||||
|
||||
// Compare by relevance first
|
||||
if (midRelevance > relevanceScore) {
|
||||
left = mid + 1;
|
||||
@@ -165,17 +165,16 @@ export function useParallelSearch(
|
||||
name: newVideos[0]?.sourceName || data.source,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.type === 'progress') {
|
||||
setCompletedSources(data.completedSources);
|
||||
setTotalVideosFound(data.totalVideosFound);
|
||||
}
|
||||
}
|
||||
else if (data.type === 'complete') {
|
||||
setLoading(false);
|
||||
|
||||
console.log(`[useParallelSearch] Search complete: ${data.totalVideosFound} videos found`);
|
||||
console.log(`[useParallelSearch] Sources with videos: ${sourcesMap.size}`);
|
||||
|
||||
|
||||
|
||||
|
||||
// Update available sources with correct property names
|
||||
const sources = Array.from(sourcesMap.entries()).map(([id, info]) => ({
|
||||
id: id, // Changed from sourceId to id
|
||||
@@ -184,20 +183,20 @@ export function useParallelSearch(
|
||||
}));
|
||||
setAvailableSources(sources);
|
||||
|
||||
console.log('[useParallelSearch] Available sources:', sources);
|
||||
|
||||
|
||||
// Apply final sorting after all results are received
|
||||
setResults((currentResults) => {
|
||||
const sorted = sortVideos(currentResults, sortBy);
|
||||
|
||||
|
||||
// Cache results
|
||||
setTimeout(() => {
|
||||
onCacheUpdate(searchQuery, sorted, sources);
|
||||
}, 100);
|
||||
|
||||
|
||||
return sorted;
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (data.type === 'error') {
|
||||
console.error('Search error:', data.message);
|
||||
setLoading(false);
|
||||
@@ -209,7 +208,7 @@ export function useParallelSearch(
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.log('Search aborted');
|
||||
|
||||
} else {
|
||||
console.error('Search error:', error);
|
||||
}
|
||||
@@ -237,7 +236,7 @@ export function useParallelSearch(
|
||||
* Load cached results
|
||||
*/
|
||||
const loadCachedResults = useCallback((cachedResults: Video[], cachedSources: any[]) => {
|
||||
console.log('[useParallelSearch] Loading cached results:', cachedResults.length, 'videos');
|
||||
|
||||
setResults(cachedResults);
|
||||
setAvailableSources(cachedSources);
|
||||
setTotalVideosFound(cachedResults.length);
|
||||
|
||||
@@ -22,10 +22,10 @@ export function useSearchCache() {
|
||||
availableSources: sources,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(cache));
|
||||
console.log('💾 Saved search to cache:', query, results.length, 'results');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to save cache:', error);
|
||||
}
|
||||
@@ -35,15 +35,15 @@ export function useSearchCache() {
|
||||
try {
|
||||
const cached = localStorage.getItem(CACHE_KEY);
|
||||
if (!cached) return null;
|
||||
|
||||
|
||||
const cache: SearchCache = JSON.parse(cached);
|
||||
|
||||
|
||||
// Check if cache is still valid
|
||||
if (Date.now() - cache.timestamp > CACHE_DURATION) {
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return cache;
|
||||
} catch (error) {
|
||||
console.error('Failed to load cache:', error);
|
||||
|
||||
@@ -44,11 +44,11 @@ export function useVideoPlayer(
|
||||
try {
|
||||
setVideoError('');
|
||||
setLoading(true);
|
||||
|
||||
|
||||
const response = await fetch(`/api/detail?id=${videoId}&source=${source}`);
|
||||
const data = await response.json();
|
||||
|
||||
console.log('Video detail API response:', data);
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
@@ -60,22 +60,17 @@ export function useVideoPlayer(
|
||||
}
|
||||
|
||||
if (data.success && data.data) {
|
||||
console.log('Video data received:', {
|
||||
id: data.data.vod_id,
|
||||
name: data.data.vod_name,
|
||||
episodeCount: data.data.episodes?.length || 0,
|
||||
firstEpisodeUrl: data.data.episodes?.[0]?.url
|
||||
});
|
||||
|
||||
|
||||
setVideoData(data.data);
|
||||
setLoading(false);
|
||||
|
||||
|
||||
if (data.data.episodes && data.data.episodes.length > 0) {
|
||||
const episodeIndex = episodeParam ? parseInt(episodeParam, 10) : 0;
|
||||
const validIndex = (episodeIndex >= 0 && episodeIndex < data.data.episodes.length) ? episodeIndex : 0;
|
||||
|
||||
|
||||
const episodeUrl = data.data.episodes[validIndex].url;
|
||||
console.log('Setting play URL for episode', validIndex, ':', episodeUrl);
|
||||
|
||||
setCurrentEpisode(validIndex);
|
||||
setPlayUrl(episodeUrl);
|
||||
} else {
|
||||
|
||||
+1
-11
@@ -50,14 +50,4 @@ export function formatLatency(latency: number): string {
|
||||
return `${latency}ms`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latency emoji indicator
|
||||
* @param latency - Response time in milliseconds
|
||||
* @returns Emoji representing speed
|
||||
*/
|
||||
export function getLatencyEmoji(latency: number): string {
|
||||
if (latency < 500) return '⚡'; // Excellent
|
||||
if (latency < 1000) return '✨'; // Good
|
||||
if (latency < 2000) return '⏱️'; // Fair
|
||||
return '🐌'; // Slow
|
||||
}
|
||||
|
||||
|
||||
Generated
-8
@@ -20,7 +20,6 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/wcag-contrast": "^3.0.3",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.3",
|
||||
"tailwindcss": "^4",
|
||||
@@ -1579,13 +1578,6 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/wcag-contrast": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/wcag-contrast/-/wcag-contrast-3.0.3.tgz",
|
||||
"integrity": "sha512-oprevfwJSLfpQK4KaWsRKJuNoebV76+xhmbXiWJGy+FkS34LpCgCMNIwRXWTb8xmmSxUE2ycFOYE7uyRVRm3LA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.46.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz",
|
||||
|
||||
Reference in New Issue
Block a user