ready to be demo

This commit is contained in:
Sewmina 2025-06-25 01:10:53 +05:30
parent b697bb176a
commit 7af4d5edc0
16 changed files with 1455 additions and 259 deletions

View File

@ -0,0 +1,63 @@
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
try {
console.log('Adding inventory item...');
const { receiptId, userId, rewardId } = await req.json();
console.log('Add inventory request:', { receiptId, userId, rewardId });
if (!receiptId || !userId || !rewardId) {
console.error('Missing required parameters:', { receiptId, userId, rewardId });
return NextResponse.json(
{ error: 'Missing required parameters' },
{ status: 400 }
);
}
// Call the external inventory API
const url = `https://vps.playpoolstudios.com/boxy/api/add_inventory_item.php?id=${receiptId}&user_id=${userId}&reward_id=${rewardId}`;
const response = await fetch(
url,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
console.log(`${url}:${response}`);
if (!response.ok) {
console.error('Failed to add inventory item:', response.status, response.statusText);
return NextResponse.json(
{ error: 'Failed to add inventory item' },
{ status: response.status }
);
}
const result = await response.json();
console.log('Inventory API response:', result);
// Check if the item was successfully added
if (result.success === false) {
console.error('Inventory item already exists or failed to add:', result);
return NextResponse.json(
{ error: 'Something went wrong - item may already exist in inventory' },
{ status: 409 }
);
}
return NextResponse.json({
success: true,
message: 'Item added to inventory successfully',
data: result,
});
} catch (error) {
console.error('Error adding inventory item:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Error adding inventory item' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,46 @@
import { NextResponse } from 'next/server';
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url);
const userId = searchParams.get('userId');
if (!userId) {
return NextResponse.json(
{ error: 'User ID is required' },
{ status: 400 }
);
}
console.log('Fetching inventory items for user:', userId);
const response = await fetch(
`https://vps.playpoolstudios.com/boxy/api/get_inventory_items.php?user_id=${userId}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
console.error('Failed to fetch inventory items:', response.status, response.statusText);
return NextResponse.json(
{ error: 'Failed to fetch inventory items' },
{ status: response.status }
);
}
const inventoryItems = await response.json();
console.log('Inventory items fetched successfully:', inventoryItems);
return NextResponse.json(inventoryItems);
} catch (error) {
console.error('Error fetching inventory items:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Error fetching inventory items' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,33 @@
import { NextResponse } from 'next/server';
export async function GET() {
try {
console.log('Fetching random reward from external API...');
const response = await fetch('https://vps.playpoolstudios.com/boxy/api/get_random_reward.php', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
console.error('Failed to fetch random reward:', response.status, response.statusText);
return NextResponse.json(
{ error: 'Failed to fetch random reward' },
{ status: response.status }
);
}
const reward = await response.json();
console.log('Random reward fetched successfully:', reward);
return NextResponse.json(reward);
} catch (error) {
console.error('Error fetching random reward:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Error fetching random reward' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,41 @@
import { NextResponse } from 'next/server';
import { getReceiptId } from '../webhooks/stripe/route';
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url);
const sessionId = searchParams.get('sessionId');
if (!sessionId) {
return NextResponse.json(
{ error: 'Session ID is required' },
{ status: 400 }
);
}
console.log('Getting receipt ID for session:', sessionId);
const receiptId = getReceiptId(sessionId);
if (!receiptId) {
console.log('No receipt ID found for session:', sessionId);
return NextResponse.json(
{ error: 'Receipt ID not found for this session' },
{ status: 404 }
);
}
console.log('Found receipt ID:', receiptId);
return NextResponse.json({
sessionId,
receiptId,
});
} catch (error) {
console.error('Error getting receipt ID:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Error getting receipt ID' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,46 @@
import { NextResponse } from 'next/server';
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url);
const rewardId = searchParams.get('id');
if (!rewardId) {
return NextResponse.json(
{ error: 'Reward ID is required' },
{ status: 400 }
);
}
console.log('Fetching reward by ID:', rewardId);
const response = await fetch(
`https://vps.playpoolstudios.com/boxy/api/get_reward_by_id.php?id=${rewardId}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
console.error('Failed to fetch reward by ID:', response.status, response.statusText);
return NextResponse.json(
{ error: 'Failed to fetch reward' },
{ status: response.status }
);
}
const reward = await response.json();
console.log('Reward fetched successfully:', reward);
return NextResponse.json(reward);
} catch (error) {
console.error('Error fetching reward by ID:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Error fetching reward' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,57 @@
import { NextResponse } from 'next/server';
import { headers } from 'next/headers';
// In-memory storage for demo purposes - in production, use a database
const paymentReceipts = new Map<string, string>();
export async function POST(req: Request) {
try {
const body = await req.text();
const headersList = await headers();
const signature = headersList.get('stripe-signature');
if (!signature) {
console.error('No Stripe signature found');
return NextResponse.json({ error: 'No signature' }, { status: 400 });
}
// For now, we'll process the webhook without signature verification
// In production, you should verify the signature using Stripe's webhook secret
const event = JSON.parse(body);
console.log('Stripe webhook event:', event.type);
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
const sessionId = session.id;
const paymentIntentId = session.payment_intent;
const boxId = session.metadata?.boxId;
console.log('Payment completed:', {
sessionId,
paymentIntentId,
boxId,
amount: session.amount_total,
currency: session.currency,
});
// Store the payment intent ID as the receipt ID
if (paymentIntentId) {
paymentReceipts.set(sessionId, paymentIntentId);
console.log(`Stored receipt ID ${paymentIntentId} for session ${sessionId}`);
}
}
return NextResponse.json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
return NextResponse.json(
{ error: 'Webhook handler failed' },
{ status: 400 }
);
}
}
// Helper function to get receipt ID for a session
export function getReceiptId(sessionId: string): string | null {
return paymentReceipts.get(sessionId) || null;
}

View File

@ -12,8 +12,6 @@ const MOCK_BOXES = [
tier: 'Small', tier: 'Small',
price: 100, price: 100,
color: 'from-blue-500 to-blue-600', color: 'from-blue-500 to-blue-600',
remaining: 2500,
total: 3000,
image: '/box-small.png', image: '/box-small.png',
description: 'Perfect for beginners. Contains common to rare items.', description: 'Perfect for beginners. Contains common to rare items.',
}, },
@ -22,8 +20,6 @@ const MOCK_BOXES = [
tier: 'Medium', tier: 'Medium',
price: 1000, price: 1000,
color: 'from-purple-500 to-purple-600', color: 'from-purple-500 to-purple-600',
remaining: 1500,
total: 2000,
image: '/box-medium.png', image: '/box-medium.png',
description: 'For serious collectors. Contains rare to epic items.', description: 'For serious collectors. Contains rare to epic items.',
}, },
@ -32,14 +28,12 @@ const MOCK_BOXES = [
tier: 'Big', tier: 'Big',
price: 10000, price: 10000,
color: 'from-pink-500 to-pink-600', color: 'from-pink-500 to-pink-600',
remaining: 500,
total: 1000,
image: '/box-big.png', image: '/box-big.png',
description: 'Premium boxes with epic to legendary items.', description: 'Premium boxes with epic to legendary items.',
}, },
]; ];
type SortOption = 'price-asc' | 'price-desc' | 'remaining-asc' | 'remaining-desc'; type SortOption = 'price-asc' | 'price-desc';
type FilterTier = 'all' | 'small' | 'medium' | 'big'; type FilterTier = 'all' | 'small' | 'medium' | 'big';
export default function BoxesPage() { export default function BoxesPage() {
@ -57,10 +51,6 @@ export default function BoxesPage() {
return a.price - b.price; return a.price - b.price;
case 'price-desc': case 'price-desc':
return b.price - a.price; return b.price - a.price;
case 'remaining-asc':
return a.remaining - b.remaining;
case 'remaining-desc':
return b.remaining - a.remaining;
default: default:
return 0; return 0;
} }
@ -147,8 +137,6 @@ export default function BoxesPage() {
> >
<option value="price-asc">Price: Low to High</option> <option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option> <option value="price-desc">Price: High to Low</option>
<option value="remaining-desc">Most Available</option>
<option value="remaining-asc">Least Available</option>
</select> </select>
</div> </div>
@ -173,20 +161,6 @@ export default function BoxesPage() {
<p className="text-foreground/60 text-sm">{box.description}</p> <p className="text-foreground/60 text-sm">{box.description}</p>
</div> </div>
{/* Progress Bar */}
<div className="space-y-1">
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Remaining</span>
<span className="font-medium">{box.remaining}/{box.total}</span>
</div>
<div className="h-2 bg-foreground/5 rounded-full overflow-hidden">
<div
className={`h-full bg-gradient-to-r ${box.color} transition-all`}
style={{ width: `${(box.remaining / box.total) * 100}%` }}
/>
</div>
</div>
{/* Price and Action */} {/* Price and Action */}
<div className="flex items-center justify-between pt-2"> <div className="flex items-center justify-between pt-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@ -4,37 +4,127 @@ import { useState, useEffect } from 'react';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import Header from '../../components/Header'; import Header from '../../components/Header';
import RewardCard from '../../components/RewardCard';
// Mock prize data - replace with actual data from your backend import { fetchRandomReward, getReceiptId, addInventoryItem, type Reward } from '../../lib/rewards';
const MOCK_PRIZES = [
{ name: 'Rare NFT', rarity: 'rare', value: 500 },
{ name: 'Epic Collectible', rarity: 'epic', value: 1000 },
{ name: 'Legendary Item', rarity: 'legendary', value: 5000 },
{ name: 'Common Token', rarity: 'common', value: 50 },
];
export default function SuccessPage() { export default function SuccessPage() {
const { user } = useAuth(); const { user } = useAuth();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [isOpening, setIsOpening] = useState(true); const [isOpening, setIsOpening] = useState(true);
const [prize, setPrize] = useState<typeof MOCK_PRIZES[0] | null>(null); const [reward, setReward] = useState<Reward | null>(null);
const [showPrize, setShowPrize] = useState(false); const [showReward, setShowReward] = useState(false);
const [error, setError] = useState<string | null>(null);
const [inventoryStatus, setInventoryStatus] = useState<'pending' | 'success' | 'error'>('pending');
const [receiptId, setReceiptId] = useState<string | null>(null);
const boxId = searchParams.get('box_id'); const boxId = searchParams.get('box_id');
const sessionId = searchParams.get('session_id'); const sessionId = searchParams.get('session_id');
useEffect(() => { useEffect(() => {
// Simulate box opening animation // Simulate box opening animation
const timer = setTimeout(() => { const timer = setTimeout(async () => {
setIsOpening(false); setIsOpening(false);
// Randomly select a prize (replace with actual logic)
const randomPrize = MOCK_PRIZES[Math.floor(Math.random() * MOCK_PRIZES.length)]; try {
setPrize(randomPrize); // Fetch real random reward from API
setShowPrize(true); const randomReward = await fetchRandomReward();
if (randomReward) {
setReward(randomReward);
setShowReward(true);
// Get receipt ID from Stripe if session ID is available
if (sessionId) {
try {
console.log('Getting receipt ID for session:', sessionId);
const stripeReceiptId = await getReceiptId(sessionId);
if (stripeReceiptId) {
setReceiptId(stripeReceiptId);
console.log('Got receipt ID from Stripe:', stripeReceiptId);
// Add reward to inventory with actual receipt ID
if (user?.uid) {
try {
console.log('Adding reward to inventory:', {
receiptId: stripeReceiptId,
userId: user.uid,
rewardId: randomReward.id
});
const success = await addInventoryItem(stripeReceiptId, user.uid, randomReward.id);
if (success) {
setInventoryStatus('success');
console.log('Reward added to inventory successfully');
} else {
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
} catch (inventoryError) {
console.error('Error adding to inventory:', inventoryError);
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
}
} else {
console.log('No receipt ID found for session, using session ID as fallback');
// Fallback to using session ID if no receipt ID is found
if (user?.uid) {
try {
const success = await addInventoryItem(sessionId, user.uid, randomReward.id);
if (success) {
setInventoryStatus('success');
} else {
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
} catch (inventoryError) {
console.error('Error adding to inventory with fallback:', inventoryError);
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
}
}
} catch (receiptError) {
console.error('Error getting receipt ID:', receiptError);
// Continue with fallback to session ID
if (user?.uid) {
try {
const success = await addInventoryItem(sessionId!, user.uid, randomReward.id);
if (success) {
setInventoryStatus('success');
} else {
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
} catch (inventoryError) {
console.error('Error adding to inventory with fallback:', inventoryError);
setInventoryStatus('error');
setError('Something went wrong');
setShowReward(false);
setReward(null);
}
}
}
}
}
} catch (err) {
console.error('Error fetching random reward:', err);
setError(err instanceof Error ? err.message : 'Failed to fetch reward');
}
}, 3000); }, 3000);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); }, [sessionId, user?.uid]);
if (!user) { if (!user) {
return ( return (
@ -50,6 +140,38 @@ export default function SuccessPage() {
); );
} }
// Show only error message if there's an error
if (error) {
return (
<>
<Header />
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 text-center">
<div className="text-6xl mb-4"></div>
<h1 className="text-3xl font-bold mb-4">Something went wrong</h1>
<p className="text-foreground/60 mb-8">
{error}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button
onClick={() => window.location.href = '/boxes'}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Back to Boxes
</button>
<button
onClick={() => window.location.reload()}
className="px-6 py-3 rounded-lg border border-foreground/20 hover:bg-foreground/5 transition-colors"
>
Try Again
</button>
</div>
</div>
</div>
</>
);
}
return ( return (
<> <>
<Header /> <Header />
@ -74,8 +196,8 @@ export default function SuccessPage() {
</div> </div>
)} )}
{/* Prize Reveal */} {/* Reward Reveal */}
{showPrize && prize && ( {showReward && reward && (
<div className="text-center"> <div className="text-center">
<div className="bg-gradient-to-br from-purple-500/10 to-pink-500/10 rounded-2xl p-8 mb-8"> <div className="bg-gradient-to-br from-purple-500/10 to-pink-500/10 rounded-2xl p-8 mb-8">
<div className="text-6xl mb-4"></div> <div className="text-6xl mb-4"></div>
@ -84,24 +206,17 @@ export default function SuccessPage() {
You found something amazing! You found something amazing!
</p> </p>
{/* Prize Card */} {/* Reward Card */}
<div className="bg-background/50 rounded-xl p-6 max-w-sm mx-auto"> <div className="max-w-sm mx-auto">
<div className="text-4xl mb-4">🎯</div> <RewardCard reward={reward} />
<h3 className="text-xl font-semibold mb-2">{prize.name}</h3>
<div className="flex items-center justify-center gap-2 mb-4">
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
prize.rarity === 'common' ? 'bg-gray-500/20 text-gray-300' :
prize.rarity === 'rare' ? 'bg-blue-500/20 text-blue-300' :
prize.rarity === 'epic' ? 'bg-purple-500/20 text-purple-300' :
'bg-yellow-500/20 text-yellow-300'
}`}>
{prize.rarity.toUpperCase()}
</span>
</div>
<p className="text-foreground/60 text-sm">
Estimated Value: ${prize.value}
</p>
</div> </div>
{/* Inventory Status */}
{inventoryStatus === 'success' && (
<div className="mt-4 p-3 bg-green-500/10 border border-green-500/20 rounded-lg">
<p className="text-green-500 text-sm"> Added to your inventory!</p>
</div>
)}
</div> </div>
{/* Action Buttons */} {/* Action Buttons */}
@ -130,6 +245,12 @@ export default function SuccessPage() {
<span className="text-foreground/60">Session ID:</span> <span className="text-foreground/60">Session ID:</span>
<span className="font-mono">{sessionId}</span> <span className="font-mono">{sessionId}</span>
</div> </div>
{receiptId && (
<div className="flex justify-between">
<span className="text-foreground/60">Receipt ID:</span>
<span className="font-mono">{receiptId}</span>
</div>
)}
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-foreground/60">Box ID:</span> <span className="text-foreground/60">Box ID:</span>
<span className="font-mono">{boxId}</span> <span className="font-mono">{boxId}</span>
@ -138,6 +259,24 @@ export default function SuccessPage() {
<span className="text-foreground/60">User:</span> <span className="text-foreground/60">User:</span>
<span>{user.email}</span> <span>{user.email}</span>
</div> </div>
{reward && (
<div className="flex justify-between">
<span className="text-foreground/60">Reward ID:</span>
<span className="font-mono">{reward.id}</span>
</div>
)}
<div className="flex justify-between">
<span className="text-foreground/60">Inventory Status:</span>
<span className={`font-medium ${
inventoryStatus === 'success' ? 'text-green-500' :
inventoryStatus === 'error' ? 'text-red-500' :
'text-yellow-500'
}`}>
{inventoryStatus === 'success' ? 'Added' :
inventoryStatus === 'error' ? 'Failed' :
'Pending'}
</span>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -58,7 +58,7 @@ export default function Header() {
{/* Profile Dropdown */} {/* Profile Dropdown */}
{isProfileMenuOpen && ( {isProfileMenuOpen && (
<div className="absolute right-0 mt-2 w-48 rounded-lg bg-background border border-foreground/10 shadow-lg py-1"> <div className="absolute right-0 mt-2 w-48 rounded-lg bg-background border border-foreground/10 shadow-lg py-1">
<Link {/* <Link
href="/profile" href="/profile"
className="block px-4 py-2 text-sm text-foreground/80 hover:bg-foreground/5 transition-colors" className="block px-4 py-2 text-sm text-foreground/80 hover:bg-foreground/5 transition-colors"
onClick={() => setIsProfileMenuOpen(false)} onClick={() => setIsProfileMenuOpen(false)}
@ -71,7 +71,7 @@ export default function Header() {
onClick={() => setIsProfileMenuOpen(false)} onClick={() => setIsProfileMenuOpen(false)}
> >
Settings Settings
</Link> </Link> */}
<button <button
onClick={handleLogout} onClick={handleLogout}
className="block w-full text-left px-4 py-2 text-sm text-red-500 hover:bg-foreground/5 transition-colors" className="block w-full text-left px-4 py-2 text-sm text-red-500 hover:bg-foreground/5 transition-colors"

View File

@ -0,0 +1,42 @@
'use client';
import { cleanImageUrl, getRarityColor, getRarityLabel, type Reward } from '../lib/rewards';
interface RewardCardProps {
reward: Reward;
showValue?: boolean;
className?: string;
}
export default function RewardCard({ reward, showValue = true, className = '' }: RewardCardProps) {
return (
<div className={`bg-background/50 rounded-xl p-6 ${className}`}>
{reward.image && (
<div className="mb-4">
<img
src={cleanImageUrl(reward.image)}
alt={reward.name}
className="w-24 h-24 object-contain mx-auto rounded-lg"
onError={(e) => {
// Fallback to emoji if image fails to load
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling?.classList.remove('hidden');
}}
/>
<div className="text-4xl mb-4 hidden">🎯</div>
</div>
)}
<h3 className="text-xl font-semibold mb-2 text-center">{reward.name}</h3>
<div className="flex items-center justify-center gap-2 mb-4">
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getRarityColor(reward.price)}`}>
{getRarityLabel(reward.price)}
</span>
</div>
{showValue && (
<p className="text-foreground/60 text-sm text-center">
Value: ${reward.price}
</p>
)}
</div>
);
}

290
app/inventory/page.tsx Normal file
View File

@ -0,0 +1,290 @@
'use client';
import { useState, useEffect } from 'react';
import { useAuth } from '../context/AuthContext';
import Header from '../components/Header';
import RewardCard from '../components/RewardCard';
import {
fetchInventoryItems,
getRewardById,
type InventoryItem,
type Reward,
formatDate
} from '../lib/rewards';
interface InventoryItemWithReward extends InventoryItem {
reward: Reward | null;
}
export default function InventoryPage() {
const { user } = useAuth();
const [inventoryItems, setInventoryItems] = useState<InventoryItemWithReward[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [selectedItem, setSelectedItem] = useState<InventoryItemWithReward | null>(null);
const [showRedeemModal, setShowRedeemModal] = useState(false);
const [showTradeModal, setShowTradeModal] = useState(false);
useEffect(() => {
const loadInventory = async () => {
if (!user?.uid) return;
setIsLoading(true);
setError(null);
try {
// Fetch inventory items
const items = await fetchInventoryItems(user.uid);
// Fetch reward details for each item
const itemsWithRewards = await Promise.all(
items.map(async (item) => {
try {
const reward = await getRewardById(item.reward_id);
return { ...item, reward };
} catch (error) {
console.error(`Error fetching reward ${item.reward_id}:`, error);
return { ...item, reward: null };
}
})
);
setInventoryItems(itemsWithRewards);
} catch (err) {
console.error('Error loading inventory:', err);
setError(err instanceof Error ? err.message : 'Failed to load inventory');
} finally {
setIsLoading(false);
}
};
loadInventory();
}, [user?.uid]);
const handleRedeem = (item: InventoryItemWithReward) => {
setSelectedItem(item);
setShowRedeemModal(true);
};
const handleTrade = (item: InventoryItemWithReward) => {
setSelectedItem(item);
setShowTradeModal(true);
};
const handleRedeemConfirm = () => {
// TODO: Implement redeem functionality
console.log('Redeeming item:', selectedItem);
alert('Redeem functionality coming soon!');
setShowRedeemModal(false);
setSelectedItem(null);
};
const handleTradeConfirm = () => {
// TODO: Implement trade functionality
console.log('Trading item:', selectedItem);
alert('Trade functionality coming soon!');
setShowTradeModal(false);
setSelectedItem(null);
};
if (!user) {
return (
<>
<Header />
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 text-center">
<h1 className="text-2xl font-bold mb-4">Access Denied</h1>
<p className="text-foreground/60">Please sign in to view your inventory.</p>
</div>
</div>
</>
);
}
return (
<>
<Header />
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Page Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">My Inventory</h1>
<p className="text-foreground/60">
Manage your collected rewards and items
</p>
</div>
{/* Loading State */}
{isLoading && (
<div className="text-center py-12">
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600 mb-4"></div>
<p className="text-foreground/60">Loading your inventory...</p>
</div>
)}
{/* Error State */}
{error && (
<div className="text-center py-12">
<div className="text-6xl mb-4"></div>
<h2 className="text-2xl font-bold mb-2">Oops!</h2>
<p className="text-foreground/60 mb-6">
{error}
</p>
<button
onClick={() => window.location.reload()}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Try Again
</button>
</div>
)}
{/* Inventory Grid */}
{!isLoading && !error && (
<>
{inventoryItems.length === 0 ? (
<div className="text-center py-12">
<div className="text-6xl mb-4">📦</div>
<h2 className="text-2xl font-bold mb-2">Empty Inventory</h2>
<p className="text-foreground/60 mb-6">
You haven&apos;t collected any rewards yet.
</p>
<button
onClick={() => window.location.href = '/boxes'}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Open Some Boxes
</button>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{inventoryItems.map((item) => (
<div
key={item.id}
className="bg-background/50 rounded-xl p-6 border border-foreground/10 hover:border-foreground/20 transition-colors"
>
{/* Reward Card */}
{item.reward ? (
<RewardCard reward={item.reward} showValue={false} className="mb-4" />
) : (
<div className="bg-foreground/5 rounded-lg p-4 mb-4 text-center">
<div className="text-4xl mb-2"></div>
<p className="text-foreground/60 text-sm">Reward details unavailable</p>
</div>
)}
{/* Item Details */}
<div className="space-y-2 mb-4">
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Receipt ID:</span>
<span
className="font-mono text-xs truncate ml-2 max-w-[120px]"
title={item.id}
>
{item.id}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Unlocked:</span>
<span className="text-foreground/80">{formatDate(item.unlocked_on)}</span>
</div>
</div>
{/* Action Buttons */}
<div className="flex gap-2">
<button
onClick={() => handleRedeem(item)}
className="flex-1 px-3 py-2 rounded-lg bg-gradient-to-r from-green-600 to-emerald-600 text-white text-sm font-medium hover:opacity-90 transition-opacity"
>
Redeem
</button>
<button
onClick={() => handleTrade(item)}
className="flex-1 px-3 py-2 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 text-white text-sm font-medium hover:opacity-90 transition-opacity"
>
Trade
</button>
</div>
</div>
))}
</div>
)}
</>
)}
</div>
</div>
{/* Redeem Modal */}
{showRedeemModal && selectedItem && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-background rounded-2xl p-6 max-w-md w-full">
<div className="text-center">
<div className="text-4xl mb-4">🎁</div>
<h2 className="text-2xl font-bold mb-2">Redeem Item</h2>
<p className="text-foreground/60 mb-6">
Are you sure you want to redeem this item?
</p>
{selectedItem.reward && (
<div className="mb-6">
<RewardCard reward={selectedItem.reward} showValue={false} />
</div>
)}
<div className="flex gap-3">
<button
onClick={() => setShowRedeemModal(false)}
className="flex-1 px-4 py-3 rounded-lg border border-foreground/20 hover:bg-foreground/5 transition-colors"
>
Cancel
</button>
<button
onClick={handleRedeemConfirm}
className="flex-1 px-4 py-3 rounded-lg bg-gradient-to-r from-green-600 to-emerald-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Redeem
</button>
</div>
</div>
</div>
</div>
)}
{/* Trade Modal */}
{showTradeModal && selectedItem && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-background rounded-2xl p-6 max-w-md w-full">
<div className="text-center">
<div className="text-4xl mb-4">🔄</div>
<h2 className="text-2xl font-bold mb-2">Trade Item</h2>
<p className="text-foreground/60 mb-6">
Are you sure you want to trade this item?
</p>
{selectedItem.reward && (
<div className="mb-6">
<RewardCard reward={selectedItem.reward} showValue={false} />
</div>
)}
<div className="flex gap-3">
<button
onClick={() => setShowTradeModal(false)}
className="flex-1 px-4 py-3 rounded-lg border border-foreground/20 hover:bg-foreground/5 transition-colors"
>
Cancel
</button>
<button
onClick={handleTradeConfirm}
className="flex-1 px-4 py-3 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Trade
</button>
</div>
</div>
</div>
</div>
)}
</>
);
}

206
app/lib/rewards.ts Normal file
View File

@ -0,0 +1,206 @@
// Types for rewards
export interface Reward {
id: string;
name: string;
price: string;
image: string;
}
// Types for inventory items
export interface InventoryItem {
id: string;
user_id: string;
reward_id: string;
unlocked_on: string;
}
// Function to fetch a random reward from the API
export async function fetchRandomReward(): Promise<Reward | null> {
try {
console.log('Fetching random reward from API...');
const response = await fetch('/api/get-random-reward', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reward = await response.json();
console.log('Random reward fetched successfully:', reward);
// Validate the reward data
if (!reward.id || !reward.name || !reward.price) {
throw new Error('Invalid reward data received');
}
return reward;
} catch (error) {
console.error('Error fetching random reward:', error);
throw error;
}
}
// Function to get receipt ID from Stripe session
export async function getReceiptId(sessionId: string): Promise<string | null> {
try {
console.log('Getting receipt ID for session:', sessionId);
const response = await fetch(`/api/get-receipt-id?sessionId=${sessionId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
if (response.status === 404) {
console.log('Receipt ID not found for session:', sessionId);
return null;
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Receipt ID retrieved successfully:', data.receiptId);
return data.receiptId;
} catch (error) {
console.error('Error getting receipt ID:', error);
throw error;
}
}
// Function to add an item to inventory
export async function addInventoryItem(receiptId: string, userId: string, rewardId: string): Promise<boolean> {
try {
console.log('Adding inventory item:', { receiptId, userId, rewardId });
const response = await fetch('/api/add-inventory-item', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
receiptId,
userId,
rewardId,
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Inventory item added successfully:', result);
return result.success === true;
} catch (error) {
console.error('Error adding inventory item:', error);
throw error;
}
}
// Function to fetch inventory items for a user
export async function fetchInventoryItems(userId: string): Promise<InventoryItem[]> {
try {
console.log('Fetching inventory items for user:', userId);
const response = await fetch(`/api/get-inventory-items?userId=${userId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const inventoryItems = await response.json();
console.log('Inventory items fetched successfully:', inventoryItems);
return inventoryItems;
} catch (error) {
console.error('Error fetching inventory items:', error);
throw error;
}
}
// Function to get reward details by ID
export async function getRewardById(rewardId: string): Promise<Reward | null> {
try {
console.log('Getting reward details for ID:', rewardId);
const response = await fetch(`/api/get-reward-by-id?id=${rewardId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
if (response.status === 404) {
console.log('Reward not found for ID:', rewardId);
return null;
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const reward = await response.json();
console.log('Reward details fetched successfully:', reward);
// Validate the reward data
if (!reward.id || !reward.name || !reward.price) {
throw new Error('Invalid reward data received');
}
return reward;
} catch (error) {
console.error('Error getting reward by ID:', error);
throw error;
}
}
// Function to clean image URL (remove escaped backslashes)
export function cleanImageUrl(url: string): string {
return url.replace(/\\/g, '');
}
// Function to get rarity color based on price
export function getRarityColor(price: string): string {
const numPrice = parseFloat(price);
if (numPrice >= 1000) return 'bg-yellow-500/20 text-yellow-300'; // Legendary
if (numPrice >= 500) return 'bg-purple-500/20 text-purple-300'; // Epic
if (numPrice >= 100) return 'bg-blue-500/20 text-blue-300'; // Rare
return 'bg-gray-500/20 text-gray-300'; // Common
}
// Function to get rarity label based on price
export function getRarityLabel(price: string): string {
const numPrice = parseFloat(price);
if (numPrice >= 1000) return 'LEGENDARY';
if (numPrice >= 500) return 'EPIC';
if (numPrice >= 100) return 'RARE';
return 'COMMON';
}
// Function to format date
export function formatDate(dateString: string): string {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}

View File

@ -83,7 +83,7 @@ export async function createCheckoutSession(
}, },
], ],
mode: 'payment', mode: 'payment',
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/boxes/success?box_id=${boxId}`, success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/boxes/success?box_id=${boxId}&session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/boxes?canceled=true`, cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/boxes?canceled=true`,
metadata: { metadata: {
boxId, boxId,

244
app/test-reward/page.tsx Normal file
View File

@ -0,0 +1,244 @@
'use client';
import { useState } from 'react';
import Header from '../components/Header';
import RewardCard from '../components/RewardCard';
import { fetchRandomReward, getReceiptId, addInventoryItem, type Reward } from '../lib/rewards';
import { useAuth } from '../context/AuthContext';
export default function TestRewardPage() {
const { user } = useAuth();
const [reward, setReward] = useState<Reward | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [inventoryStatus, setInventoryStatus] = useState<'idle' | 'pending' | 'success' | 'error'>('idle');
const [receiptId, setReceiptId] = useState('test-receipt-123');
const [sessionId, setSessionId] = useState('cs_test_1234567890');
const [stripeReceiptId, setStripeReceiptId] = useState<string | null>(null);
const handleGetRandomReward = async () => {
setIsLoading(true);
setError(null);
setReward(null);
setInventoryStatus('idle');
setStripeReceiptId(null);
try {
const randomReward = await fetchRandomReward();
if (randomReward) {
setReward(randomReward);
}
} catch (err) {
console.error('Error fetching random reward:', err);
setError(err instanceof Error ? err.message : 'Failed to fetch reward');
} finally {
setIsLoading(false);
}
};
const handleGetReceiptId = async () => {
if (!sessionId) {
setError('Please enter a session ID');
return;
}
try {
const receipt = await getReceiptId(sessionId);
setStripeReceiptId(receipt);
if (receipt) {
console.log('Got receipt ID from Stripe:', receipt);
} else {
console.log('No receipt ID found for session:', sessionId);
}
} catch (err) {
console.error('Error getting receipt ID:', err);
setError(err instanceof Error ? err.message : 'Failed to get receipt ID');
}
};
const handleAddToInventory = async () => {
if (!reward || !user?.uid) {
setError('No reward or user available');
return;
}
setInventoryStatus('pending');
setError(null);
// Use Stripe receipt ID if available, otherwise use manual receipt ID
const finalReceiptId = stripeReceiptId || receiptId;
try {
const success = await addInventoryItem(finalReceiptId, user.uid, reward.id);
if (success) {
setInventoryStatus('success');
} else {
setInventoryStatus('error');
setError('Failed to add to inventory');
}
} catch (err) {
console.error('Error adding to inventory:', err);
setInventoryStatus('error');
setError(err instanceof Error ? err.message : 'Failed to add to inventory');
}
};
return (
<>
<Header />
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Page Header */}
<div className="text-center mb-8">
<h1 className="text-3xl font-bold mb-2">Test Random Reward</h1>
<p className="text-foreground/60">
Test the random reward API functionality with Stripe receipt IDs
</p>
</div>
{/* Input Fields */}
<div className="text-center mb-8 space-y-4">
<div>
<label htmlFor="sessionId" className="block text-sm font-medium mb-2">
Stripe Session ID (for testing receipt ID):
</label>
<input
id="sessionId"
type="text"
value={sessionId}
onChange={(e) => setSessionId(e.target.value)}
className="px-4 py-2 rounded-lg bg-foreground/5 border border-foreground/10 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
placeholder="Enter Stripe session ID"
/>
</div>
<div>
<label htmlFor="receiptId" className="block text-sm font-medium mb-2">
Manual Receipt ID (fallback):
</label>
<input
id="receiptId"
type="text"
value={receiptId}
onChange={(e) => setReceiptId(e.target.value)}
className="px-4 py-2 rounded-lg bg-foreground/5 border border-foreground/10 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
placeholder="Enter manual receipt ID"
/>
</div>
</div>
{/* Test Buttons */}
<div className="text-center mb-8 space-y-4">
<button
onClick={handleGetRandomReward}
disabled={isLoading}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity disabled:opacity-50 mr-4"
>
{isLoading ? 'Fetching...' : 'Get Random Reward'}
</button>
<button
onClick={handleGetReceiptId}
disabled={!sessionId}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 text-white font-semibold hover:opacity-90 transition-opacity disabled:opacity-50 mr-4"
>
Get Receipt ID from Stripe
</button>
{reward && user && (
<button
onClick={handleAddToInventory}
disabled={inventoryStatus === 'pending'}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-green-600 to-blue-600 text-white font-semibold hover:opacity-90 transition-opacity disabled:opacity-50"
>
{inventoryStatus === 'pending' ? 'Adding...' : 'Add to Inventory'}
</button>
)}
</div>
{/* Receipt ID Display */}
{stripeReceiptId && (
<div className="text-center mb-8">
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4">
<p className="text-blue-500 text-sm">
Stripe Receipt ID: <span className="font-mono">{stripeReceiptId}</span>
</p>
</div>
</div>
)}
{/* Error Message */}
{error && (
<div className="text-center mb-8">
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-4">
<p className="text-red-500">{error}</p>
</div>
</div>
)}
{/* Inventory Status */}
{inventoryStatus !== 'idle' && (
<div className="text-center mb-8">
<div className={`p-3 rounded-lg border ${
inventoryStatus === 'success' ? 'bg-green-500/10 border-green-500/20' :
inventoryStatus === 'error' ? 'bg-red-500/10 border-red-500/20' :
'bg-yellow-500/10 border-yellow-500/20'
}`}>
<p className={`text-sm ${
inventoryStatus === 'success' ? 'text-green-500' :
inventoryStatus === 'error' ? 'text-red-500' :
'text-yellow-500'
}`}>
{inventoryStatus === 'success' ? '✅ Added to inventory successfully!' :
inventoryStatus === 'error' ? '❌ Failed to add to inventory' :
'⏳ Adding to inventory...'}
</p>
</div>
</div>
)}
{/* Reward Display */}
{reward && (
<div className="text-center">
<h2 className="text-2xl font-bold mb-6">Random Reward</h2>
<div className="max-w-sm mx-auto">
<RewardCard reward={reward} />
</div>
</div>
)}
{/* API Info */}
<div className="mt-12 bg-foreground/5 rounded-xl p-6">
<h3 className="text-lg font-semibold mb-4">API Information</h3>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-foreground/60">Random Reward Endpoint:</span>
<span className="font-mono">/api/get-random-reward</span>
</div>
<div className="flex justify-between">
<span className="text-foreground/60">Get Receipt ID Endpoint:</span>
<span className="font-mono">/api/get-receipt-id</span>
</div>
<div className="flex justify-between">
<span className="text-foreground/60">Add Inventory Endpoint:</span>
<span className="font-mono">/api/add-inventory-item</span>
</div>
<div className="flex justify-between">
<span className="text-foreground/60">Stripe Webhook:</span>
<span className="font-mono">/api/webhooks/stripe</span>
</div>
<div className="flex justify-between">
<span className="text-foreground/60">External Reward API:</span>
<span className="font-mono text-xs">vps.playpoolstudios.com/boxy/api/get_random_reward.php</span>
</div>
<div className="flex justify-between">
<span className="text-foreground/60">External Inventory API:</span>
<span className="font-mono text-xs">vps.playpoolstudios.com/boxy/api/add_inventory_item.php</span>
</div>
</div>
</div>
</div>
</div>
</>
);
}

399
package-lock.json generated
View File

@ -1657,22 +1657,6 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@next/swc-win32-x64-msvc": {
"version": "15.3.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz",
"integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1810,9 +1794,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@stripe/stripe-js": { "node_modules/@stripe/stripe-js": {
"version": "7.3.1", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.3.1.tgz", "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.4.0.tgz",
"integrity": "sha512-pTzb864TQWDRQBPLgSPFRoyjSDUqpCkbEgTzpsjiTjGz1Z5SxZNXJek28w1s6Dyry4CyW4/Izj5jHE/J9hCJYQ==", "integrity": "sha512-lQHQPfXPTBeh0XFjq6PqSBAyR7umwcJbvJhXV77uGCUDD6ymXJU/f2164ydLMLCCceNuPlbV9b+1smx98efwWQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12.16" "node": ">=12.16"
@ -2078,7 +2062,7 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@tailwindcss/oxide-win32-x64-msvc": { "node_modules/@tailwindcss/oxide/node_modules/@tailwindcss/oxide-win32-x64-msvc": {
"version": "4.1.10", "version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz",
"integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==", "integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==",
@ -2319,17 +2303,17 @@
"optional": true "optional": true
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz",
"integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "integrity": "sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/type-utils": "8.34.0", "@typescript-eslint/type-utils": "8.35.0",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.35.0",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.35.0",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^7.0.0", "ignore": "^7.0.0",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -2343,7 +2327,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/parser": "^8.34.0", "@typescript-eslint/parser": "^8.35.0",
"eslint": "^8.57.0 || ^9.0.0", "eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0" "typescript": ">=4.8.4 <5.9.0"
} }
@ -2359,16 +2343,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.0.tgz",
"integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.35.0",
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.35.0",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.35.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -2384,14 +2368,14 @@
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.0.tgz",
"integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", "integrity": "sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.34.0", "@typescript-eslint/tsconfig-utils": "^8.35.0",
"@typescript-eslint/types": "^8.34.0", "@typescript-eslint/types": "^8.35.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -2406,14 +2390,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.0.tgz",
"integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "integrity": "sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.35.0",
"@typescript-eslint/visitor-keys": "8.34.0" "@typescript-eslint/visitor-keys": "8.35.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2424,9 +2408,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.0.tgz",
"integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", "integrity": "sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -2441,14 +2425,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.0.tgz",
"integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "integrity": "sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.34.0", "@typescript-eslint/typescript-estree": "8.35.0",
"@typescript-eslint/utils": "8.34.0", "@typescript-eslint/utils": "8.35.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.1.0" "ts-api-utils": "^2.1.0"
}, },
@ -2465,9 +2449,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.0.tgz",
"integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "integrity": "sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -2479,16 +2463,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.0.tgz",
"integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "integrity": "sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.34.0", "@typescript-eslint/project-service": "8.35.0",
"@typescript-eslint/tsconfig-utils": "8.34.0", "@typescript-eslint/tsconfig-utils": "8.35.0",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.35.0",
"@typescript-eslint/visitor-keys": "8.34.0", "@typescript-eslint/visitor-keys": "8.35.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -2564,16 +2548,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.0.tgz",
"integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "integrity": "sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.7.0", "@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.35.0",
"@typescript-eslint/typescript-estree": "8.34.0" "@typescript-eslint/typescript-estree": "8.35.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2588,14 +2572,14 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.34.0", "version": "8.35.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.0.tgz",
"integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "integrity": "sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.34.0", "@typescript-eslint/types": "8.35.0",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2606,9 +2590,9 @@
} }
}, },
"node_modules/@unrs/resolver-binding-android-arm-eabi": { "node_modules/@unrs/resolver-binding-android-arm-eabi": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz",
"integrity": "sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==", "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2620,9 +2604,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-android-arm64": { "node_modules/@unrs/resolver-binding-android-arm64": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz",
"integrity": "sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==", "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2634,9 +2618,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-darwin-arm64": { "node_modules/@unrs/resolver-binding-darwin-arm64": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.2.tgz",
"integrity": "sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==", "integrity": "sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2648,9 +2632,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-darwin-x64": { "node_modules/@unrs/resolver-binding-darwin-x64": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz",
"integrity": "sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==", "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2662,9 +2646,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-freebsd-x64": { "node_modules/@unrs/resolver-binding-freebsd-x64": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz",
"integrity": "sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==", "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2676,9 +2660,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz",
"integrity": "sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==", "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2690,9 +2674,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz",
"integrity": "sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==", "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2704,9 +2688,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-arm64-gnu": { "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz",
"integrity": "sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==", "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2718,9 +2702,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-arm64-musl": { "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz",
"integrity": "sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==", "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2732,9 +2716,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz",
"integrity": "sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==", "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -2746,9 +2730,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz",
"integrity": "sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==", "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -2760,9 +2744,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-riscv64-musl": { "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz",
"integrity": "sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==", "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -2774,9 +2758,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-s390x-gnu": { "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz",
"integrity": "sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==", "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -2788,9 +2772,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-x64-gnu": { "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz",
"integrity": "sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==", "integrity": "sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2802,9 +2786,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-linux-x64-musl": { "node_modules/@unrs/resolver-binding-linux-x64-musl": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.2.tgz",
"integrity": "sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==", "integrity": "sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2816,9 +2800,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-wasm32-wasi": { "node_modules/@unrs/resolver-binding-wasm32-wasi": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz",
"integrity": "sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==", "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==",
"cpu": [ "cpu": [
"wasm32" "wasm32"
], ],
@ -2833,9 +2817,9 @@
} }
}, },
"node_modules/@unrs/resolver-binding-win32-arm64-msvc": { "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz",
"integrity": "sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==", "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2847,9 +2831,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-win32-ia32-msvc": { "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz",
"integrity": "sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==", "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -2861,9 +2845,9 @@
] ]
}, },
"node_modules/@unrs/resolver-binding-win32-x64-msvc": { "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz",
"integrity": "sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==", "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -3353,9 +3337,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001723", "version": "1.0.30001724",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz",
"integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -3712,9 +3696,9 @@
} }
}, },
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.18.1", "version": "5.18.2",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
"integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4068,9 +4052,9 @@
} }
}, },
"node_modules/eslint-module-utils": { "node_modules/eslint-module-utils": {
"version": "2.12.0", "version": "2.12.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
"integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4096,30 +4080,30 @@
} }
}, },
"node_modules/eslint-plugin-import": { "node_modules/eslint-plugin-import": {
"version": "2.31.0", "version": "2.32.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@rtsao/scc": "^1.1.0", "@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.8", "array-includes": "^3.1.9",
"array.prototype.findlastindex": "^1.2.5", "array.prototype.findlastindex": "^1.2.6",
"array.prototype.flat": "^1.3.2", "array.prototype.flat": "^1.3.3",
"array.prototype.flatmap": "^1.3.2", "array.prototype.flatmap": "^1.3.3",
"debug": "^3.2.7", "debug": "^3.2.7",
"doctrine": "^2.1.0", "doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.9", "eslint-import-resolver-node": "^0.3.9",
"eslint-module-utils": "^2.12.0", "eslint-module-utils": "^2.12.1",
"hasown": "^2.0.2", "hasown": "^2.0.2",
"is-core-module": "^2.15.1", "is-core-module": "^2.16.1",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"minimatch": "^3.1.2", "minimatch": "^3.1.2",
"object.fromentries": "^2.0.8", "object.fromentries": "^2.0.8",
"object.groupby": "^1.0.3", "object.groupby": "^1.0.3",
"object.values": "^1.2.0", "object.values": "^1.2.1",
"semver": "^6.3.1", "semver": "^6.3.1",
"string.prototype.trimend": "^1.0.8", "string.prototype.trimend": "^1.0.9",
"tsconfig-paths": "^3.15.0" "tsconfig-paths": "^3.15.0"
}, },
"engines": { "engines": {
@ -4569,9 +4553,9 @@
} }
}, },
"node_modules/firebase-admin/node_modules/@types/node": { "node_modules/firebase-admin/node_modules/@types/node": {
"version": "22.15.32", "version": "22.15.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.33.tgz",
"integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", "integrity": "sha512-wzoocdnnpSxZ+6CjW4ADCK1jVmd1S/J3ArNWfn8FDDQtRm8dkDg7TA+mvek2wNrfCgwuZxqEOiB9B1XCJ6+dbw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
@ -6081,7 +6065,7 @@
"url": "https://opencollective.com/parcel" "url": "https://opencollective.com/parcel"
} }
}, },
"node_modules/lightningcss-win32-x64-msvc": { "node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": {
"version": "1.30.1", "version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
"integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
@ -6215,12 +6199,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/lru-cache/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
"node_modules/lru-memoizer": { "node_modules/lru-memoizer": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz",
@ -6473,6 +6451,22 @@
} }
} }
}, },
"node_modules/next/node_modules/@next/swc-win32-x64-msvc": {
"version": "15.3.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz",
"integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/next/node_modules/postcss": { "node_modules/next/node_modules/postcss": {
"version": "8.4.31", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@ -7785,6 +7779,16 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/tar/node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
},
"node_modules/teeny-request": { "node_modules/teeny-request": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
@ -8070,38 +8074,38 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/unrs-resolver": { "node_modules/unrs-resolver": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.0.tgz", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.2.tgz",
"integrity": "sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==", "integrity": "sha512-VUyWiTNQD7itdiMuJy+EuLEErLj3uwX/EpHQF8EOf33Dq3Ju6VW1GXm+swk6+1h7a49uv9fKZ+dft9jU7esdLA==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"napi-postinstall": "^0.2.2" "napi-postinstall": "^0.2.4"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/unrs-resolver" "url": "https://opencollective.com/unrs-resolver"
}, },
"optionalDependencies": { "optionalDependencies": {
"@unrs/resolver-binding-android-arm-eabi": "1.9.0", "@unrs/resolver-binding-android-arm-eabi": "1.9.2",
"@unrs/resolver-binding-android-arm64": "1.9.0", "@unrs/resolver-binding-android-arm64": "1.9.2",
"@unrs/resolver-binding-darwin-arm64": "1.9.0", "@unrs/resolver-binding-darwin-arm64": "1.9.2",
"@unrs/resolver-binding-darwin-x64": "1.9.0", "@unrs/resolver-binding-darwin-x64": "1.9.2",
"@unrs/resolver-binding-freebsd-x64": "1.9.0", "@unrs/resolver-binding-freebsd-x64": "1.9.2",
"@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.0", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.2",
"@unrs/resolver-binding-linux-arm-musleabihf": "1.9.0", "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.2",
"@unrs/resolver-binding-linux-arm64-gnu": "1.9.0", "@unrs/resolver-binding-linux-arm64-gnu": "1.9.2",
"@unrs/resolver-binding-linux-arm64-musl": "1.9.0", "@unrs/resolver-binding-linux-arm64-musl": "1.9.2",
"@unrs/resolver-binding-linux-ppc64-gnu": "1.9.0", "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.2",
"@unrs/resolver-binding-linux-riscv64-gnu": "1.9.0", "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.2",
"@unrs/resolver-binding-linux-riscv64-musl": "1.9.0", "@unrs/resolver-binding-linux-riscv64-musl": "1.9.2",
"@unrs/resolver-binding-linux-s390x-gnu": "1.9.0", "@unrs/resolver-binding-linux-s390x-gnu": "1.9.2",
"@unrs/resolver-binding-linux-x64-gnu": "1.9.0", "@unrs/resolver-binding-linux-x64-gnu": "1.9.2",
"@unrs/resolver-binding-linux-x64-musl": "1.9.0", "@unrs/resolver-binding-linux-x64-musl": "1.9.2",
"@unrs/resolver-binding-wasm32-wasi": "1.9.0", "@unrs/resolver-binding-wasm32-wasi": "1.9.2",
"@unrs/resolver-binding-win32-arm64-msvc": "1.9.0", "@unrs/resolver-binding-win32-arm64-msvc": "1.9.2",
"@unrs/resolver-binding-win32-ia32-msvc": "1.9.0", "@unrs/resolver-binding-win32-ia32-msvc": "1.9.2",
"@unrs/resolver-binding-win32-x64-msvc": "1.9.0" "@unrs/resolver-binding-win32-x64-msvc": "1.9.2"
} }
}, },
"node_modules/uri-js": { "node_modules/uri-js": {
@ -8328,14 +8332,10 @@
} }
}, },
"node_modules/yallist": { "node_modules/yallist": {
"version": "5.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true, "license": "ISC"
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
}, },
"node_modules/yargs": { "node_modules/yargs": {
"version": "17.7.2", "version": "17.7.2",
@ -8376,6 +8376,21 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "15.3.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz",
"integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
} }
} }
} }

View File

@ -3,9 +3,9 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack", "dev": "next dev --turbopack -p 6014",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start -p 6014",
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {