ly0303521

添加白名单功能

@@ -4,7 +4,7 @@ import os @@ -4,7 +4,7 @@ import os
4 import secrets 4 import secrets
5 import time 5 import time
6 from pathlib import Path 6 from pathlib import Path
7 -from threading import Lock 7 +from threading import Lock, RLock
8 from typing import List, Literal, Optional 8 from typing import List, Literal, Optional
9 9
10 import httpx 10 import httpx
@@ -20,6 +20,7 @@ Z_IMAGE_BASE_URL = os.getenv("Z_IMAGE_BASE_URL", "http://106.120.52.146:39009"). @@ -20,6 +20,7 @@ Z_IMAGE_BASE_URL = os.getenv("Z_IMAGE_BASE_URL", "http://106.120.52.146:39009").
20 REQUEST_TIMEOUT_SECONDS = float(os.getenv("REQUEST_TIMEOUT_SECONDS", "120")) 20 REQUEST_TIMEOUT_SECONDS = float(os.getenv("REQUEST_TIMEOUT_SECONDS", "120"))
21 GALLERY_DATA_PATH = Path(os.getenv("GALLERY_DATA_PATH", Path(__file__).with_name("gallery_data.json"))) 21 GALLERY_DATA_PATH = Path(os.getenv("GALLERY_DATA_PATH", Path(__file__).with_name("gallery_data.json")))
22 GALLERY_MAX_ITEMS = int(os.getenv("GALLERY_MAX_ITEMS", "500")) 22 GALLERY_MAX_ITEMS = int(os.getenv("GALLERY_MAX_ITEMS", "500"))
  23 +WHITELIST_PATH = Path(os.getenv("WHITELIST_PATH", Path(__file__).with_name("whitelist.txt")))
23 24
24 25
25 class ImageGenerationPayload(BaseModel): 26 class ImageGenerationPayload(BaseModel):
@@ -67,6 +68,55 @@ class GalleryImage(BaseModel): @@ -67,6 +68,55 @@ class GalleryImage(BaseModel):
67 ImageGenerationResponse.model_rebuild() 68 ImageGenerationResponse.model_rebuild()
68 69
69 70
  71 +class WhitelistStore:
  72 + """Simple text file backed whitelist."""
  73 +
  74 + def __init__(self, path: Path) -> None:
  75 + self.path = path
  76 + self.lock = RLock()
  77 + if not self.path.exists():
  78 + # Default admin ID if file doesn't exist
  79 + self._write(["86427531"])
  80 +
  81 + def _read(self) -> List[str]:
  82 + if not self.path.exists():
  83 + return []
  84 + try:
  85 + with self.path.open("r", encoding="utf-8") as f:
  86 + lines = f.read().splitlines()
  87 + return [line.strip() for line in lines if line.strip()]
  88 + except OSError:
  89 + return []
  90 +
  91 + def _write(self, ids: List[str]) -> None:
  92 + with self.lock:
  93 + try:
  94 + with self.path.open("w", encoding="utf-8") as f:
  95 + f.write("\n".join(ids))
  96 + except OSError as exc:
  97 + print(f"[WARN] Failed to write whitelist: {exc}")
  98 +
  99 + def is_allowed(self, user_id: str) -> bool:
  100 + allowed = self._read()
  101 + return user_id in allowed
  102 +
  103 + def add_users(self, user_ids: List[str]) -> None:
  104 + with self.lock:
  105 + current = set(self._read())
  106 + current.update(user_ids)
  107 + self._write(sorted(list(current)))
  108 +
  109 + def remove_user(self, user_id: str) -> None:
  110 + with self.lock:
  111 + current = self._read()
  112 + if user_id in current:
  113 + current = [uid for uid in current if uid != user_id]
  114 + self._write(current)
  115 +
  116 + def get_all(self) -> List[str]:
  117 + return self._read()
  118 +
  119 +
70 class GalleryStore: 120 class GalleryStore:
71 """Simple JSON file backed store for generated images.""" 121 """Simple JSON file backed store for generated images."""
72 122
@@ -157,6 +207,7 @@ class GalleryStore: @@ -157,6 +207,7 @@ class GalleryStore:
157 207
158 208
159 gallery_store = GalleryStore(GALLERY_DATA_PATH, GALLERY_MAX_ITEMS) 209 gallery_store = GalleryStore(GALLERY_DATA_PATH, GALLERY_MAX_ITEMS)
  210 +whitelist_store = WhitelistStore(WHITELIST_PATH)
160 211
161 212
162 app = FastAPI(title="Z-Image Proxy", version="1.0.0") 213 app = FastAPI(title="Z-Image Proxy", version="1.0.0")
@@ -186,6 +237,30 @@ async def health() -> dict: @@ -186,6 +237,30 @@ async def health() -> dict:
186 return {"status": "ok"} 237 return {"status": "ok"}
187 238
188 239
  240 +@app.post("/auth/login")
  241 +async def login(user_id: str = Query(..., alias="userId")) -> dict:
  242 + if whitelist_store.is_allowed(user_id):
  243 + return {"status": "ok", "userId": user_id}
  244 + raise HTTPException(status_code=403, detail="User not whitelisted")
  245 +
  246 +
  247 +@app.get("/admin/whitelist")
  248 +async def get_whitelist() -> dict:
  249 + return {"whitelist": whitelist_store.get_all()}
  250 +
  251 +
  252 +@app.post("/admin/whitelist")
  253 +async def add_whitelist(user_ids: List[str]) -> dict:
  254 + whitelist_store.add_users(user_ids)
  255 + return {"status": "ok", "whitelist": whitelist_store.get_all()}
  256 +
  257 +
  258 +@app.delete("/admin/whitelist/{user_id}")
  259 +async def remove_whitelist(user_id: str) -> dict:
  260 + whitelist_store.remove_user(user_id)
  261 + return {"status": "ok", "whitelist": whitelist_store.get_all()}
  262 +
  263 +
189 @app.post("/likes/{image_id}") 264 @app.post("/likes/{image_id}")
190 async def toggle_like( 265 async def toggle_like(
191 image_id: str, 266 image_id: str,
1 -#!/usr/bin/env python  
2 -# -*- coding: utf-8 -*-  
3 -"""Client script to test Z-Image server."""  
4 -  
5 -import base64  
6 -import time  
7 -import requests  
8 -from PIL import Image  
9 -import io  
10 -  
11 -  
12 -def test_health_check(base_url="http://localhost:9009"):  
13 - """Test server health check."""  
14 - print("Testing health check...")  
15 - try:  
16 - response = requests.get(f"{base_url}/health", timeout=10)  
17 - print(f"Status code: {response.status_code}")  
18 - print(f"Response text: {response.text}")  
19 - print(f"Response headers: {dict(response.headers)}")  
20 - if response.status_code == 200:  
21 - print(f"Health check: {response.json()}")  
22 - else:  
23 - print(f"Error: HTTP {response.status_code}")  
24 - print(f"Response: {response.text}")  
25 - except requests.exceptions.RequestException as e:  
26 - print(f"Request error: {e}")  
27 - raise  
28 - except ValueError as e:  
29 - print(f"JSON decode error: {e}")  
30 - print(f"Response content: {response.text if 'response' in locals() else 'No response'}")  
31 - raise  
32 - print()  
33 -  
34 -  
35 -def test_generate_image(base_url="http://localhost:9009", save_path="generated_image.png", output_format="base64"):  
36 - """Test image generation with base64 or URL response."""  
37 - format_name = "base64" if output_format == "base64" else "URL"  
38 - print(f"Testing image generation ({format_name})...")  
39 -  
40 - # Prepare request  
41 - request_data = {  
42 - "prompt": "一只可爱的熊猫在竹林里吃竹子,阳光透过树叶洒下,4K高清,摄影作品",  
43 - "height": 1024,  
44 - "width": 1024,  
45 - "num_inference_steps": 8,  
46 - "guidance_scale": 0.0,  
47 - "seed": 42,  
48 - "output_format": output_format,  
49 - }  
50 -  
51 - print(f"Prompt: {request_data['prompt']}")  
52 - print(f"Size: {request_data['width']}x{request_data['height']}")  
53 - print(f"Output format: {output_format}")  
54 -  
55 - # Send request  
56 - start_time = time.time()  
57 - response = requests.post(f"{base_url}/generate", json=request_data)  
58 - end_time = time.time()  
59 -  
60 - if response.status_code == 200:  
61 - result = response.json()  
62 - print(f"Generation successful!")  
63 - print(f"Time taken: {result['time_taken']:.2f}s")  
64 - print(f"Total request time: {end_time - start_time:.2f}s")  
65 -  
66 - if output_format == "url":  
67 - # Handle URL response  
68 - if result.get("url"):  
69 - image_url = result["url"]  
70 - print(f"Image URL: {image_url}")  
71 -  
72 - # Download image from URL and save  
73 - try:  
74 - img_response = requests.get(image_url, timeout=10)  
75 - if img_response.status_code == 200:  
76 - img = Image.open(io.BytesIO(img_response.content))  
77 - img.save(save_path)  
78 - print(f"Image downloaded and saved to: {save_path}")  
79 - else:  
80 - print(f"Failed to download image from URL: HTTP {img_response.status_code}")  
81 - except Exception as e:  
82 - print(f"Error downloading image from URL: {e}")  
83 - else:  
84 - print("Warning: No URL returned in response")  
85 - # Fallback: check if base64 is available  
86 - if result.get("image"):  
87 - print("Fallback: Using base64 image from response")  
88 - img_data = base64.b64decode(result["image"])  
89 - img = Image.open(io.BytesIO(img_data))  
90 - img.save(save_path)  
91 - print(f"Image saved to: {save_path}")  
92 - else:  
93 - # Handle base64 response  
94 - if result.get("image"):  
95 - img_data = base64.b64decode(result["image"])  
96 - img = Image.open(io.BytesIO(img_data))  
97 - img.save(save_path)  
98 - print(f"Image saved to: {save_path}")  
99 - else:  
100 - print("Warning: No image data in response")  
101 - else:  
102 - print(f"Error: {response.status_code}")  
103 - print(response.json())  
104 -  
105 - print()  
106 -  
107 -  
108 -def test_generate_stream(base_url="http://localhost:9009", save_path="generated_stream.png"):  
109 - """Test image generation with stream response."""  
110 - print("Testing image generation (stream)...")  
111 -  
112 - # Prepare request  
113 - request_data = {  
114 - "prompt": "A futuristic cityscape at sunset, flying cars, neon lights, cyberpunk style, highly detailed",  
115 - "height": 1024,  
116 - "width": 1024,  
117 - "num_inference_steps": 8,  
118 - "guidance_scale": 0.0,  
119 - "seed": 123,  
120 - }  
121 -  
122 - print(f"Prompt: {request_data['prompt']}")  
123 - print(f"Size: {request_data['width']}x{request_data['height']}")  
124 -  
125 - # Send request  
126 - start_time = time.time()  
127 - response = requests.post(f"{base_url}/generate_stream", json=request_data)  
128 - end_time = time.time()  
129 -  
130 - if response.status_code == 200:  
131 - print(f"Generation successful!")  
132 - print(f"Total request time: {end_time - start_time:.2f}s")  
133 -  
134 - # Save image  
135 - img = Image.open(io.BytesIO(response.content))  
136 - img.save(save_path)  
137 - print(f"Image saved to: {save_path}")  
138 - else:  
139 - print(f"Error: {response.status_code}")  
140 - print(response.text)  
141 -  
142 - print()  
143 -  
144 -  
145 -def main():  
146 - """Run all tests."""  
147 - base_url = "http://106.120.52.146:39009"  
148 -  
149 - print("=" * 60)  
150 - print("Z-Image Server Client Test")  
151 - print("=" * 60)  
152 - print()  
153 -  
154 - # Test health check  
155 - try:  
156 - test_health_check(base_url)  
157 - except Exception as e:  
158 - print(f"Health check failed: {e}")  
159 - print("Make sure the server is running!")  
160 - return  
161 -  
162 - # Test image generation (base64)  
163 - try:  
164 - test_generate_image(base_url, "test_output_base64.png", output_format="base64")  
165 - except Exception as e:  
166 - print(f"Image generation test (base64) failed: {e}")  
167 -  
168 - # Test image generation (URL)  
169 - try:  
170 - test_generate_image(base_url, "test_output_url.png", output_format="url")  
171 - except Exception as e:  
172 - print(f"Image generation test (URL) failed: {e}")  
173 -  
174 - # Test image generation (stream)  
175 - try:  
176 - test_generate_stream(base_url, "test_output_stream.png")  
177 - except Exception as e:  
178 - print(f"Stream generation test failed: {e}")  
179 -  
180 - print("=" * 60)  
181 - print("All tests completed!")  
182 - print("=" * 60)  
183 -  
184 -  
185 -if __name__ == "__main__":  
186 - main()  
187 - 1 +#!/usr/bin/env python
  2 +# -*- coding: utf-8 -*-
  3 +"""Client script to test Z-Image server."""
  4 +
  5 +import base64
  6 +import time
  7 +import requests
  8 +from PIL import Image
  9 +import io
  10 +
  11 +
  12 +def test_health_check(base_url="http://localhost:9009"):
  13 + """Test server health check."""
  14 + print("Testing health check...")
  15 + try:
  16 + response = requests.get(f"{base_url}/health", timeout=10)
  17 + print(f"Status code: {response.status_code}")
  18 + print(f"Response text: {response.text}")
  19 + print(f"Response headers: {dict(response.headers)}")
  20 + if response.status_code == 200:
  21 + print(f"Health check: {response.json()}")
  22 + else:
  23 + print(f"Error: HTTP {response.status_code}")
  24 + print(f"Response: {response.text}")
  25 + except requests.exceptions.RequestException as e:
  26 + print(f"Request error: {e}")
  27 + raise
  28 + except ValueError as e:
  29 + print(f"JSON decode error: {e}")
  30 + print(f"Response content: {response.text if 'response' in locals() else 'No response'}")
  31 + raise
  32 + print()
  33 +
  34 +
  35 +def test_generate_image(base_url="http://localhost:9009", save_path="generated_image.png", output_format="base64"):
  36 + """Test image generation with base64 or URL response."""
  37 + format_name = "base64" if output_format == "base64" else "URL"
  38 + print(f"Testing image generation ({format_name})...")
  39 +
  40 + # Prepare request
  41 + request_data = {
  42 + "prompt": "一只可爱的熊猫在竹林里吃竹子,阳光透过树叶洒下,4K高清,摄影作品",
  43 + "height": 1024,
  44 + "width": 1024,
  45 + "num_inference_steps": 8,
  46 + "guidance_scale": 0.0,
  47 + "seed": 42,
  48 + "output_format": output_format,
  49 + }
  50 +
  51 + print(f"Prompt: {request_data['prompt']}")
  52 + print(f"Size: {request_data['width']}x{request_data['height']}")
  53 + print(f"Output format: {output_format}")
  54 +
  55 + # Send request
  56 + start_time = time.time()
  57 + response = requests.post(f"{base_url}/generate", json=request_data)
  58 + end_time = time.time()
  59 +
  60 + if response.status_code == 200:
  61 + result = response.json()
  62 + print(f"Generation successful!")
  63 + print(f"Time taken: {result['time_taken']:.2f}s")
  64 + print(f"Total request time: {end_time - start_time:.2f}s")
  65 +
  66 + if output_format == "url":
  67 + # Handle URL response
  68 + if result.get("url"):
  69 + image_url = result["url"]
  70 + print(f"Image URL: {image_url}")
  71 +
  72 + # Download image from URL and save
  73 + try:
  74 + img_response = requests.get(image_url, timeout=10)
  75 + if img_response.status_code == 200:
  76 + img = Image.open(io.BytesIO(img_response.content))
  77 + img.save(save_path)
  78 + print(f"Image downloaded and saved to: {save_path}")
  79 + else:
  80 + print(f"Failed to download image from URL: HTTP {img_response.status_code}")
  81 + except Exception as e:
  82 + print(f"Error downloading image from URL: {e}")
  83 + else:
  84 + print("Warning: No URL returned in response")
  85 + # Fallback: check if base64 is available
  86 + if result.get("image"):
  87 + print("Fallback: Using base64 image from response")
  88 + img_data = base64.b64decode(result["image"])
  89 + img = Image.open(io.BytesIO(img_data))
  90 + img.save(save_path)
  91 + print(f"Image saved to: {save_path}")
  92 + else:
  93 + # Handle base64 response
  94 + if result.get("image"):
  95 + img_data = base64.b64decode(result["image"])
  96 + img = Image.open(io.BytesIO(img_data))
  97 + img.save(save_path)
  98 + print(f"Image saved to: {save_path}")
  99 + else:
  100 + print("Warning: No image data in response")
  101 + else:
  102 + print(f"Error: {response.status_code}")
  103 + print(response.json())
  104 +
  105 + print()
  106 +
  107 +
  108 +def test_generate_stream(base_url="http://localhost:9009", save_path="generated_stream.png"):
  109 + """Test image generation with stream response."""
  110 + print("Testing image generation (stream)...")
  111 +
  112 + # Prepare request
  113 + request_data = {
  114 + "prompt": "A futuristic cityscape at sunset, flying cars, neon lights, cyberpunk style, highly detailed",
  115 + "height": 1024,
  116 + "width": 1024,
  117 + "num_inference_steps": 8,
  118 + "guidance_scale": 0.0,
  119 + "seed": 123,
  120 + }
  121 +
  122 + print(f"Prompt: {request_data['prompt']}")
  123 + print(f"Size: {request_data['width']}x{request_data['height']}")
  124 +
  125 + # Send request
  126 + start_time = time.time()
  127 + response = requests.post(f"{base_url}/generate_stream", json=request_data)
  128 + end_time = time.time()
  129 +
  130 + if response.status_code == 200:
  131 + print(f"Generation successful!")
  132 + print(f"Total request time: {end_time - start_time:.2f}s")
  133 +
  134 + # Save image
  135 + img = Image.open(io.BytesIO(response.content))
  136 + img.save(save_path)
  137 + print(f"Image saved to: {save_path}")
  138 + else:
  139 + print(f"Error: {response.status_code}")
  140 + print(response.text)
  141 +
  142 + print()
  143 +
  144 +
  145 +def main():
  146 + """Run all tests."""
  147 + base_url = "http://106.120.52.146:39009"
  148 +
  149 + print("=" * 60)
  150 + print("Z-Image Server Client Test")
  151 + print("=" * 60)
  152 + print()
  153 +
  154 + # Test health check
  155 + try:
  156 + test_health_check(base_url)
  157 + except Exception as e:
  158 + print(f"Health check failed: {e}")
  159 + print("Make sure the server is running!")
  160 + return
  161 +
  162 + # Test image generation (base64)
  163 + try:
  164 + test_generate_image(base_url, "test_output_base64.png", output_format="base64")
  165 + except Exception as e:
  166 + print(f"Image generation test (base64) failed: {e}")
  167 +
  168 + # Test image generation (URL)
  169 + try:
  170 + test_generate_image(base_url, "test_output_url.png", output_format="url")
  171 + except Exception as e:
  172 + print(f"Image generation test (URL) failed: {e}")
  173 +
  174 + # Test image generation (stream)
  175 + try:
  176 + test_generate_stream(base_url, "test_output_stream.png")
  177 + except Exception as e:
  178 + print(f"Stream generation test failed: {e}")
  179 +
  180 + print("=" * 60)
  181 + print("All tests completed!")
  182 + print("=" * 60)
  183 +
  184 +
  185 +if __name__ == "__main__":
  186 + main()
  187 +
@@ -9,7 +9,8 @@ import HistoryBar from './components/HistoryBar'; @@ -9,7 +9,8 @@ import HistoryBar from './components/HistoryBar';
9 import DetailModal from './components/DetailModal'; 9 import DetailModal from './components/DetailModal';
10 import AdminModal from './components/AdminModal'; 10 import AdminModal from './components/AdminModal';
11 import AuthModal from './components/AuthModal'; 11 import AuthModal from './components/AuthModal';
12 -import { Loader2, Trash2, User as UserIcon, Save, Settings, Sparkles } from 'lucide-react'; 12 +import WhitelistModal from './components/WhitelistModal';
  13 +import { Loader2, Trash2, User as UserIcon, Save, Settings, Sparkles, Users } from 'lucide-react';
13 14
14 const STORAGE_KEY_DATA = 'z-image-gallery-data-v2'; 15 const STORAGE_KEY_DATA = 'z-image-gallery-data-v2';
15 const STORAGE_KEY_USER = 'z-image-user-profile'; 16 const STORAGE_KEY_USER = 'z-image-user-profile';
@@ -19,6 +20,7 @@ const App: React.FC = () => { @@ -19,6 +20,7 @@ const App: React.FC = () => {
19 // --- State: User --- 20 // --- State: User ---
20 const [currentUser, setCurrentUser] = useState<UserProfile | null>(null); 21 const [currentUser, setCurrentUser] = useState<UserProfile | null>(null);
21 const [isAuthModalOpen, setIsAuthModalOpen] = useState(false); 22 const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
  23 + const [isWhitelistModalOpen, setIsWhitelistModalOpen] = useState(false);
22 24
23 // --- State: Data --- 25 // --- State: Data ---
24 const [images, setImages] = useState<ImageItem[]>(() => { 26 const [images, setImages] = useState<ImageItem[]>(() => {
@@ -266,6 +268,7 @@ export const SHOWCASE_IMAGES: ImageItem[] = ${JSON.stringify(exportData, null, 2 @@ -266,6 +268,7 @@ export const SHOWCASE_IMAGES: ImageItem[] = ${JSON.stringify(exportData, null, 2
266 {isAdmin && ( 268 {isAdmin && (
267 <div className="flex gap-2"> 269 <div className="flex gap-2">
268 <button onClick={handleExportShowcase} className="hidden md:flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-full hover:bg-purple-700 transition-colors text-sm font-medium shadow-sm"><Save size={16} /><span>导出库</span></button> 270 <button onClick={handleExportShowcase} className="hidden md:flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-full hover:bg-purple-700 transition-colors text-sm font-medium shadow-sm"><Save size={16} /><span>导出库</span></button>
  271 + <button onClick={() => setIsWhitelistModalOpen(true)} className="hidden md:flex items-center justify-center p-2 rounded-full bg-gray-100 text-gray-700 hover:bg-gray-200 transition-colors" title="白名单管理"><Users size={20} /></button>
269 <button onClick={handleResetData} className="p-2 md:p-3 rounded-full hover:bg-red-50 text-gray-300 hover:text-red-500 transition-colors"><Trash2 size={20} /></button> 272 <button onClick={handleResetData} className="p-2 md:p-3 rounded-full hover:bg-red-50 text-gray-300 hover:text-red-500 transition-colors"><Trash2 size={20} /></button>
270 <button onClick={handleOpenCreateModal} className="p-2 md:p-3 rounded-full bg-black text-white hover:bg-gray-800 transition-colors shadow-sm"><Settings size={20} /></button> 273 <button onClick={handleOpenCreateModal} className="p-2 md:p-3 rounded-full bg-black text-white hover:bg-gray-800 transition-colors shadow-sm"><Settings size={20} /></button>
271 </div> 274 </div>
@@ -305,6 +308,7 @@ export const SHOWCASE_IMAGES: ImageItem[] = ${JSON.stringify(exportData, null, 2 @@ -305,6 +308,7 @@ export const SHOWCASE_IMAGES: ImageItem[] = ${JSON.stringify(exportData, null, 2
305 )} 308 )}
306 309
307 <AdminModal isOpen={isAdminModalOpen} onClose={() => setIsAdminModalOpen(false)} onSave={handleSaveImage} onDelete={handleDeleteImage} initialData={editingImage} /> 310 <AdminModal isOpen={isAdminModalOpen} onClose={() => setIsAdminModalOpen(false)} onSave={handleSaveImage} onDelete={handleDeleteImage} initialData={editingImage} />
  311 + <WhitelistModal isOpen={isWhitelistModalOpen} onClose={() => setIsWhitelistModalOpen(false)} />
308 </div> 312 </div>
309 ); 313 );
310 }; 314 };
1 import React, { useState } from 'react'; 1 import React, { useState } from 'react';
2 -import { User, Lock } from 'lucide-react'; 2 +import { User, Lock, Loader2 } from 'lucide-react';
  3 +import { login } from '../services/authService';
3 4
4 interface AuthModalProps { 5 interface AuthModalProps {
5 isOpen: boolean; 6 isOpen: boolean;
@@ -9,18 +10,32 @@ interface AuthModalProps { @@ -9,18 +10,32 @@ interface AuthModalProps {
9 const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => { 10 const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => {
10 const [inputId, setInputId] = useState(''); 11 const [inputId, setInputId] = useState('');
11 const [error, setError] = useState(''); 12 const [error, setError] = useState('');
  13 + const [isLoading, setIsLoading] = useState(false);
12 14
13 if (!isOpen) return null; 15 if (!isOpen) return null;
14 16
15 - const handleSubmit = (e: React.FormEvent) => { 17 + const handleSubmit = async (e: React.FormEvent) => {
16 e.preventDefault(); 18 e.preventDefault();
17 - // Validate 8-digit requirement  
18 - const regex = /^\d{8}$/;  
19 - if (!regex.test(inputId)) {  
20 - setError('工号必须是8位数字');  
21 - return; 19 + if (!inputId.trim()) {
  20 + setError('请输入工号');
  21 + return;
  22 + }
  23 +
  24 + setIsLoading(true);
  25 + setError('');
  26 +
  27 + try {
  28 + const success = await login(inputId.trim());
  29 + if (success) {
  30 + onLogin(inputId.trim());
  31 + } else {
  32 + setError('验证失败:工号未在白名单中');
  33 + }
  34 + } catch (e) {
  35 + setError('服务器连接失败,请稍后重试');
  36 + } finally {
  37 + setIsLoading(false);
22 } 38 }
23 - onLogin(inputId);  
24 }; 39 };
25 40
26 return ( 41 return (
@@ -37,7 +52,7 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => { @@ -37,7 +52,7 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => {
37 欢迎来到 艺云-DESIGN 52 欢迎来到 艺云-DESIGN
38 </h2> 53 </h2>
39 <p className="text-gray-500 mb-8"> 54 <p className="text-gray-500 mb-8">
40 - 请输入您的8位工号以访问平台功能 55 + 请输入您的工号以访问平台功能
41 </p> 56 </p>
42 57
43 <form onSubmit={handleSubmit} className="space-y-4"> 58 <form onSubmit={handleSubmit} className="space-y-4">
@@ -50,9 +65,9 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => { @@ -50,9 +65,9 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => {
50 setInputId(e.target.value); 65 setInputId(e.target.value);
51 setError(''); 66 setError('');
52 }} 67 }}
53 - placeholder="请输入8位工号 (例如: 10023456)" 68 + placeholder="请输入工号"
54 className="w-full pl-10 pr-4 py-3 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl focus:ring-2 focus:ring-black dark:focus:ring-white outline-none transition-all font-mono" 69 className="w-full pl-10 pr-4 py-3 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl focus:ring-2 focus:ring-black dark:focus:ring-white outline-none transition-all font-mono"
55 - maxLength={8} 70 + disabled={isLoading}
56 /> 71 />
57 </div> 72 </div>
58 73
@@ -62,9 +77,11 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => { @@ -62,9 +77,11 @@ const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onLogin }) => {
62 77
63 <button 78 <button
64 type="submit" 79 type="submit"
65 - className="w-full py-3 bg-black dark:bg-white text-white dark:text-black rounded-xl font-bold hover:opacity-90 transition-opacity" 80 + disabled={isLoading}
  81 + className="w-full py-3 bg-black dark:bg-white text-white dark:text-black rounded-xl font-bold hover:opacity-90 transition-opacity flex items-center justify-center gap-2"
66 > 82 >
67 - 进入系统 83 + {isLoading && <Loader2 className="animate-spin" size={20} />}
  84 + {isLoading ? '验证中...' : '进入系统'}
68 </button> 85 </button>
69 </form> 86 </form>
70 </div> 87 </div>
  1 +import React, { useState, useEffect } from 'react';
  2 +import { X, Plus, RefreshCw, Trash2 } from 'lucide-react';
  3 +import { getWhitelist, addWhitelist, removeWhitelist } from '../services/authService';
  4 +
  5 +interface WhitelistModalProps {
  6 + isOpen: boolean;
  7 + onClose: () => void;
  8 +}
  9 +
  10 +const WhitelistModal: React.FC<WhitelistModalProps> = ({ isOpen, onClose }) => {
  11 + const [whitelist, setWhitelist] = useState<string[]>([]);
  12 + const [newIds, setNewIds] = useState('');
  13 + const [isLoading, setIsLoading] = useState(false);
  14 + const [error, setError] = useState<string | null>(null);
  15 +
  16 + useEffect(() => {
  17 + if (isOpen) {
  18 + loadWhitelist();
  19 + }
  20 + }, [isOpen]);
  21 +
  22 + const loadWhitelist = async () => {
  23 + setIsLoading(true);
  24 + try {
  25 + const data = await getWhitelist();
  26 + setWhitelist(data);
  27 + setError(null);
  28 + } catch (e) {
  29 + setError('无法获取白名单');
  30 + } finally {
  31 + setIsLoading(false);
  32 + }
  33 + };
  34 +
  35 + const handleAdd = async () => {
  36 + if (!newIds.trim()) return;
  37 +
  38 + // Split by newlines, commas, or spaces
  39 + const idsToAdd = newIds
  40 + .split(/[\n, ]+/)
  41 + .map(s => s.trim())
  42 + .filter(s => s.length > 0);
  43 +
  44 + if (idsToAdd.length === 0) return;
  45 +
  46 + setIsLoading(true);
  47 + try {
  48 + const updated = await addWhitelist(idsToAdd);
  49 + setWhitelist(updated);
  50 + setNewIds('');
  51 + setError(null);
  52 + } catch (e) {
  53 + setError('添加失败');
  54 + } finally {
  55 + setIsLoading(false);
  56 + }
  57 + };
  58 +
  59 + const handleDelete = async (id: string) => {
  60 + if (!confirm(`确定要移除用户 ${id} 吗?`)) return;
  61 +
  62 + setIsLoading(true);
  63 + try {
  64 + const updated = await removeWhitelist(id);
  65 + setWhitelist(updated);
  66 + setError(null);
  67 + } catch (e) {
  68 + setError('移除失败');
  69 + } finally {
  70 + setIsLoading(false);
  71 + }
  72 + };
  73 +
  74 + if (!isOpen) return null;
  75 +
  76 + return (
  77 + <div className="fixed inset-0 z-[110] flex items-center justify-center p-4">
  78 + <div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
  79 +
  80 + <div className="relative bg-white dark:bg-gray-900 w-full max-w-2xl rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-[80vh]">
  81 + <div className="flex justify-between items-center p-6 border-b border-gray-100 dark:border-gray-800">
  82 + <h2 className="text-xl font-bold text-gray-800 dark:text-white flex items-center gap-2">
  83 + 用户白名单管理
  84 + <span className="text-sm font-normal text-gray-500 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded-full">
  85 + {whitelist.length} 人
  86 + </span>
  87 + </h2>
  88 + <button onClick={onClose} className="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full">
  89 + <X size={20} />
  90 + </button>
  91 + </div>
  92 +
  93 + <div className="flex-1 overflow-hidden flex flex-col p-6 gap-6">
  94 + {/* Add Section */}
  95 + <div className="space-y-3">
  96 + <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
  97 + 批量添加 (每行一个,或用逗号/空格分隔)
  98 + </label>
  99 + <div className="flex gap-3">
  100 + <textarea
  101 + value={newIds}
  102 + onChange={(e) => setNewIds(e.target.value)}
  103 + placeholder="例如:\n1001\n1002\n1003"
  104 + className="flex-1 p-3 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl focus:ring-2 focus:ring-black dark:focus:ring-white outline-none min-h-[100px] font-mono text-sm resize-none"
  105 + />
  106 + </div>
  107 + <button
  108 + onClick={handleAdd}
  109 + disabled={isLoading || !newIds.trim()}
  110 + className="w-full py-2 bg-black dark:bg-white text-white dark:text-black rounded-lg hover:opacity-90 transition-opacity font-medium disabled:opacity-50 flex items-center justify-center gap-2"
  111 + >
  112 + <Plus size={18} />
  113 + 添加至白名单
  114 + </button>
  115 + </div>
  116 +
  117 + {/* List Section */}
  118 + <div className="flex-1 overflow-y-auto border border-gray-100 dark:border-gray-800 rounded-xl bg-gray-50 dark:bg-gray-800/50 p-4">
  119 + <div className="flex justify-between items-center mb-3">
  120 + <h3 className="font-medium text-gray-700 dark:text-gray-300 text-sm">已授权用户</h3>
  121 + <button onClick={loadWhitelist} className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 p-1">
  122 + <RefreshCw size={14} className={isLoading ? "animate-spin" : ""} />
  123 + </button>
  124 + </div>
  125 +
  126 + {error ? (
  127 + <div className="text-red-500 text-center py-4 text-sm">{error}</div>
  128 + ) : (
  129 + <div className="grid grid-cols-2 md:grid-cols-3 gap-2">
  130 + {whitelist.map(id => (
  131 + <div key={id} className="bg-white dark:bg-gray-800 px-3 py-2 rounded border border-gray-100 dark:border-gray-700 flex justify-between items-center group">
  132 + <span className="font-mono text-sm">{id}</span>
  133 + <button
  134 + onClick={() => handleDelete(id)}
  135 + className="text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity p-1"
  136 + title="移除用户"
  137 + >
  138 + <Trash2 size={14} />
  139 + </button>
  140 + </div>
  141 + ))}
  142 + </div>
  143 + )}
  144 + </div>
  145 + </div>
  146 + </div>
  147 + </div>
  148 + );
  149 +};
  150 +
  151 +export default WhitelistModal;
  1 +{
  2 + "name": "z-image-generator",
  3 + "version": "0.0.0",
  4 + "lockfileVersion": 3,
  5 + "requires": true,
  6 + "packages": {
  7 + "": {
  8 + "name": "z-image-generator",
  9 + "version": "0.0.0",
  10 + "dependencies": {
  11 + "lucide-react": "^0.561.0",
  12 + "react": "^19.2.3",
  13 + "react-dom": "^19.2.3"
  14 + },
  15 + "devDependencies": {
  16 + "@types/node": "^22.14.0",
  17 + "@vitejs/plugin-react": "^5.0.0",
  18 + "typescript": "~5.8.2",
  19 + "vite": "^6.2.0"
  20 + }
  21 + },
  22 + "node_modules/@babel/code-frame": {
  23 + "version": "7.27.1",
  24 + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
  25 + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
  26 + "dev": true,
  27 + "license": "MIT",
  28 + "dependencies": {
  29 + "@babel/helper-validator-identifier": "^7.27.1",
  30 + "js-tokens": "^4.0.0",
  31 + "picocolors": "^1.1.1"
  32 + },
  33 + "engines": {
  34 + "node": ">=6.9.0"
  35 + }
  36 + },
  37 + "node_modules/@babel/compat-data": {
  38 + "version": "7.28.5",
  39 + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
  40 + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
  41 + "dev": true,
  42 + "license": "MIT",
  43 + "engines": {
  44 + "node": ">=6.9.0"
  45 + }
  46 + },
  47 + "node_modules/@babel/core": {
  48 + "version": "7.28.5",
  49 + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
  50 + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
  51 + "dev": true,
  52 + "license": "MIT",
  53 + "peer": true,
  54 + "dependencies": {
  55 + "@babel/code-frame": "^7.27.1",
  56 + "@babel/generator": "^7.28.5",
  57 + "@babel/helper-compilation-targets": "^7.27.2",
  58 + "@babel/helper-module-transforms": "^7.28.3",
  59 + "@babel/helpers": "^7.28.4",
  60 + "@babel/parser": "^7.28.5",
  61 + "@babel/template": "^7.27.2",
  62 + "@babel/traverse": "^7.28.5",
  63 + "@babel/types": "^7.28.5",
  64 + "@jridgewell/remapping": "^2.3.5",
  65 + "convert-source-map": "^2.0.0",
  66 + "debug": "^4.1.0",
  67 + "gensync": "^1.0.0-beta.2",
  68 + "json5": "^2.2.3",
  69 + "semver": "^6.3.1"
  70 + },
  71 + "engines": {
  72 + "node": ">=6.9.0"
  73 + },
  74 + "funding": {
  75 + "type": "opencollective",
  76 + "url": "https://opencollective.com/babel"
  77 + }
  78 + },
  79 + "node_modules/@babel/generator": {
  80 + "version": "7.28.5",
  81 + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
  82 + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
  83 + "dev": true,
  84 + "license": "MIT",
  85 + "dependencies": {
  86 + "@babel/parser": "^7.28.5",
  87 + "@babel/types": "^7.28.5",
  88 + "@jridgewell/gen-mapping": "^0.3.12",
  89 + "@jridgewell/trace-mapping": "^0.3.28",
  90 + "jsesc": "^3.0.2"
  91 + },
  92 + "engines": {
  93 + "node": ">=6.9.0"
  94 + }
  95 + },
  96 + "node_modules/@babel/helper-compilation-targets": {
  97 + "version": "7.27.2",
  98 + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
  99 + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
  100 + "dev": true,
  101 + "license": "MIT",
  102 + "dependencies": {
  103 + "@babel/compat-data": "^7.27.2",
  104 + "@babel/helper-validator-option": "^7.27.1",
  105 + "browserslist": "^4.24.0",
  106 + "lru-cache": "^5.1.1",
  107 + "semver": "^6.3.1"
  108 + },
  109 + "engines": {
  110 + "node": ">=6.9.0"
  111 + }
  112 + },
  113 + "node_modules/@babel/helper-globals": {
  114 + "version": "7.28.0",
  115 + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
  116 + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
  117 + "dev": true,
  118 + "license": "MIT",
  119 + "engines": {
  120 + "node": ">=6.9.0"
  121 + }
  122 + },
  123 + "node_modules/@babel/helper-module-imports": {
  124 + "version": "7.27.1",
  125 + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
  126 + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
  127 + "dev": true,
  128 + "license": "MIT",
  129 + "dependencies": {
  130 + "@babel/traverse": "^7.27.1",
  131 + "@babel/types": "^7.27.1"
  132 + },
  133 + "engines": {
  134 + "node": ">=6.9.0"
  135 + }
  136 + },
  137 + "node_modules/@babel/helper-module-transforms": {
  138 + "version": "7.28.3",
  139 + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
  140 + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
  141 + "dev": true,
  142 + "license": "MIT",
  143 + "dependencies": {
  144 + "@babel/helper-module-imports": "^7.27.1",
  145 + "@babel/helper-validator-identifier": "^7.27.1",
  146 + "@babel/traverse": "^7.28.3"
  147 + },
  148 + "engines": {
  149 + "node": ">=6.9.0"
  150 + },
  151 + "peerDependencies": {
  152 + "@babel/core": "^7.0.0"
  153 + }
  154 + },
  155 + "node_modules/@babel/helper-plugin-utils": {
  156 + "version": "7.27.1",
  157 + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
  158 + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
  159 + "dev": true,
  160 + "license": "MIT",
  161 + "engines": {
  162 + "node": ">=6.9.0"
  163 + }
  164 + },
  165 + "node_modules/@babel/helper-string-parser": {
  166 + "version": "7.27.1",
  167 + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
  168 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
  169 + "dev": true,
  170 + "license": "MIT",
  171 + "engines": {
  172 + "node": ">=6.9.0"
  173 + }
  174 + },
  175 + "node_modules/@babel/helper-validator-identifier": {
  176 + "version": "7.28.5",
  177 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
  178 + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
  179 + "dev": true,
  180 + "license": "MIT",
  181 + "engines": {
  182 + "node": ">=6.9.0"
  183 + }
  184 + },
  185 + "node_modules/@babel/helper-validator-option": {
  186 + "version": "7.27.1",
  187 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
  188 + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
  189 + "dev": true,
  190 + "license": "MIT",
  191 + "engines": {
  192 + "node": ">=6.9.0"
  193 + }
  194 + },
  195 + "node_modules/@babel/helpers": {
  196 + "version": "7.28.4",
  197 + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
  198 + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
  199 + "dev": true,
  200 + "license": "MIT",
  201 + "dependencies": {
  202 + "@babel/template": "^7.27.2",
  203 + "@babel/types": "^7.28.4"
  204 + },
  205 + "engines": {
  206 + "node": ">=6.9.0"
  207 + }
  208 + },
  209 + "node_modules/@babel/parser": {
  210 + "version": "7.28.5",
  211 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
  212 + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
  213 + "dev": true,
  214 + "license": "MIT",
  215 + "dependencies": {
  216 + "@babel/types": "^7.28.5"
  217 + },
  218 + "bin": {
  219 + "parser": "bin/babel-parser.js"
  220 + },
  221 + "engines": {
  222 + "node": ">=6.0.0"
  223 + }
  224 + },
  225 + "node_modules/@babel/plugin-transform-react-jsx-self": {
  226 + "version": "7.27.1",
  227 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
  228 + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
  229 + "dev": true,
  230 + "license": "MIT",
  231 + "dependencies": {
  232 + "@babel/helper-plugin-utils": "^7.27.1"
  233 + },
  234 + "engines": {
  235 + "node": ">=6.9.0"
  236 + },
  237 + "peerDependencies": {
  238 + "@babel/core": "^7.0.0-0"
  239 + }
  240 + },
  241 + "node_modules/@babel/plugin-transform-react-jsx-source": {
  242 + "version": "7.27.1",
  243 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
  244 + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
  245 + "dev": true,
  246 + "license": "MIT",
  247 + "dependencies": {
  248 + "@babel/helper-plugin-utils": "^7.27.1"
  249 + },
  250 + "engines": {
  251 + "node": ">=6.9.0"
  252 + },
  253 + "peerDependencies": {
  254 + "@babel/core": "^7.0.0-0"
  255 + }
  256 + },
  257 + "node_modules/@babel/template": {
  258 + "version": "7.27.2",
  259 + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
  260 + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
  261 + "dev": true,
  262 + "license": "MIT",
  263 + "dependencies": {
  264 + "@babel/code-frame": "^7.27.1",
  265 + "@babel/parser": "^7.27.2",
  266 + "@babel/types": "^7.27.1"
  267 + },
  268 + "engines": {
  269 + "node": ">=6.9.0"
  270 + }
  271 + },
  272 + "node_modules/@babel/traverse": {
  273 + "version": "7.28.5",
  274 + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
  275 + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
  276 + "dev": true,
  277 + "license": "MIT",
  278 + "dependencies": {
  279 + "@babel/code-frame": "^7.27.1",
  280 + "@babel/generator": "^7.28.5",
  281 + "@babel/helper-globals": "^7.28.0",
  282 + "@babel/parser": "^7.28.5",
  283 + "@babel/template": "^7.27.2",
  284 + "@babel/types": "^7.28.5",
  285 + "debug": "^4.3.1"
  286 + },
  287 + "engines": {
  288 + "node": ">=6.9.0"
  289 + }
  290 + },
  291 + "node_modules/@babel/types": {
  292 + "version": "7.28.5",
  293 + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
  294 + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
  295 + "dev": true,
  296 + "license": "MIT",
  297 + "dependencies": {
  298 + "@babel/helper-string-parser": "^7.27.1",
  299 + "@babel/helper-validator-identifier": "^7.28.5"
  300 + },
  301 + "engines": {
  302 + "node": ">=6.9.0"
  303 + }
  304 + },
  305 + "node_modules/@esbuild/aix-ppc64": {
  306 + "version": "0.25.12",
  307 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
  308 + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
  309 + "cpu": [
  310 + "ppc64"
  311 + ],
  312 + "dev": true,
  313 + "license": "MIT",
  314 + "optional": true,
  315 + "os": [
  316 + "aix"
  317 + ],
  318 + "engines": {
  319 + "node": ">=18"
  320 + }
  321 + },
  322 + "node_modules/@esbuild/android-arm": {
  323 + "version": "0.25.12",
  324 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
  325 + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
  326 + "cpu": [
  327 + "arm"
  328 + ],
  329 + "dev": true,
  330 + "license": "MIT",
  331 + "optional": true,
  332 + "os": [
  333 + "android"
  334 + ],
  335 + "engines": {
  336 + "node": ">=18"
  337 + }
  338 + },
  339 + "node_modules/@esbuild/android-arm64": {
  340 + "version": "0.25.12",
  341 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
  342 + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
  343 + "cpu": [
  344 + "arm64"
  345 + ],
  346 + "dev": true,
  347 + "license": "MIT",
  348 + "optional": true,
  349 + "os": [
  350 + "android"
  351 + ],
  352 + "engines": {
  353 + "node": ">=18"
  354 + }
  355 + },
  356 + "node_modules/@esbuild/android-x64": {
  357 + "version": "0.25.12",
  358 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
  359 + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
  360 + "cpu": [
  361 + "x64"
  362 + ],
  363 + "dev": true,
  364 + "license": "MIT",
  365 + "optional": true,
  366 + "os": [
  367 + "android"
  368 + ],
  369 + "engines": {
  370 + "node": ">=18"
  371 + }
  372 + },
  373 + "node_modules/@esbuild/darwin-arm64": {
  374 + "version": "0.25.12",
  375 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
  376 + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
  377 + "cpu": [
  378 + "arm64"
  379 + ],
  380 + "dev": true,
  381 + "license": "MIT",
  382 + "optional": true,
  383 + "os": [
  384 + "darwin"
  385 + ],
  386 + "engines": {
  387 + "node": ">=18"
  388 + }
  389 + },
  390 + "node_modules/@esbuild/darwin-x64": {
  391 + "version": "0.25.12",
  392 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
  393 + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
  394 + "cpu": [
  395 + "x64"
  396 + ],
  397 + "dev": true,
  398 + "license": "MIT",
  399 + "optional": true,
  400 + "os": [
  401 + "darwin"
  402 + ],
  403 + "engines": {
  404 + "node": ">=18"
  405 + }
  406 + },
  407 + "node_modules/@esbuild/freebsd-arm64": {
  408 + "version": "0.25.12",
  409 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
  410 + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
  411 + "cpu": [
  412 + "arm64"
  413 + ],
  414 + "dev": true,
  415 + "license": "MIT",
  416 + "optional": true,
  417 + "os": [
  418 + "freebsd"
  419 + ],
  420 + "engines": {
  421 + "node": ">=18"
  422 + }
  423 + },
  424 + "node_modules/@esbuild/freebsd-x64": {
  425 + "version": "0.25.12",
  426 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
  427 + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
  428 + "cpu": [
  429 + "x64"
  430 + ],
  431 + "dev": true,
  432 + "license": "MIT",
  433 + "optional": true,
  434 + "os": [
  435 + "freebsd"
  436 + ],
  437 + "engines": {
  438 + "node": ">=18"
  439 + }
  440 + },
  441 + "node_modules/@esbuild/linux-arm": {
  442 + "version": "0.25.12",
  443 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
  444 + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
  445 + "cpu": [
  446 + "arm"
  447 + ],
  448 + "dev": true,
  449 + "license": "MIT",
  450 + "optional": true,
  451 + "os": [
  452 + "linux"
  453 + ],
  454 + "engines": {
  455 + "node": ">=18"
  456 + }
  457 + },
  458 + "node_modules/@esbuild/linux-arm64": {
  459 + "version": "0.25.12",
  460 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
  461 + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
  462 + "cpu": [
  463 + "arm64"
  464 + ],
  465 + "dev": true,
  466 + "license": "MIT",
  467 + "optional": true,
  468 + "os": [
  469 + "linux"
  470 + ],
  471 + "engines": {
  472 + "node": ">=18"
  473 + }
  474 + },
  475 + "node_modules/@esbuild/linux-ia32": {
  476 + "version": "0.25.12",
  477 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
  478 + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
  479 + "cpu": [
  480 + "ia32"
  481 + ],
  482 + "dev": true,
  483 + "license": "MIT",
  484 + "optional": true,
  485 + "os": [
  486 + "linux"
  487 + ],
  488 + "engines": {
  489 + "node": ">=18"
  490 + }
  491 + },
  492 + "node_modules/@esbuild/linux-loong64": {
  493 + "version": "0.25.12",
  494 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
  495 + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
  496 + "cpu": [
  497 + "loong64"
  498 + ],
  499 + "dev": true,
  500 + "license": "MIT",
  501 + "optional": true,
  502 + "os": [
  503 + "linux"
  504 + ],
  505 + "engines": {
  506 + "node": ">=18"
  507 + }
  508 + },
  509 + "node_modules/@esbuild/linux-mips64el": {
  510 + "version": "0.25.12",
  511 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
  512 + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
  513 + "cpu": [
  514 + "mips64el"
  515 + ],
  516 + "dev": true,
  517 + "license": "MIT",
  518 + "optional": true,
  519 + "os": [
  520 + "linux"
  521 + ],
  522 + "engines": {
  523 + "node": ">=18"
  524 + }
  525 + },
  526 + "node_modules/@esbuild/linux-ppc64": {
  527 + "version": "0.25.12",
  528 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
  529 + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
  530 + "cpu": [
  531 + "ppc64"
  532 + ],
  533 + "dev": true,
  534 + "license": "MIT",
  535 + "optional": true,
  536 + "os": [
  537 + "linux"
  538 + ],
  539 + "engines": {
  540 + "node": ">=18"
  541 + }
  542 + },
  543 + "node_modules/@esbuild/linux-riscv64": {
  544 + "version": "0.25.12",
  545 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
  546 + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
  547 + "cpu": [
  548 + "riscv64"
  549 + ],
  550 + "dev": true,
  551 + "license": "MIT",
  552 + "optional": true,
  553 + "os": [
  554 + "linux"
  555 + ],
  556 + "engines": {
  557 + "node": ">=18"
  558 + }
  559 + },
  560 + "node_modules/@esbuild/linux-s390x": {
  561 + "version": "0.25.12",
  562 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
  563 + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
  564 + "cpu": [
  565 + "s390x"
  566 + ],
  567 + "dev": true,
  568 + "license": "MIT",
  569 + "optional": true,
  570 + "os": [
  571 + "linux"
  572 + ],
  573 + "engines": {
  574 + "node": ">=18"
  575 + }
  576 + },
  577 + "node_modules/@esbuild/linux-x64": {
  578 + "version": "0.25.12",
  579 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
  580 + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
  581 + "cpu": [
  582 + "x64"
  583 + ],
  584 + "dev": true,
  585 + "license": "MIT",
  586 + "optional": true,
  587 + "os": [
  588 + "linux"
  589 + ],
  590 + "engines": {
  591 + "node": ">=18"
  592 + }
  593 + },
  594 + "node_modules/@esbuild/netbsd-arm64": {
  595 + "version": "0.25.12",
  596 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
  597 + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
  598 + "cpu": [
  599 + "arm64"
  600 + ],
  601 + "dev": true,
  602 + "license": "MIT",
  603 + "optional": true,
  604 + "os": [
  605 + "netbsd"
  606 + ],
  607 + "engines": {
  608 + "node": ">=18"
  609 + }
  610 + },
  611 + "node_modules/@esbuild/netbsd-x64": {
  612 + "version": "0.25.12",
  613 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
  614 + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
  615 + "cpu": [
  616 + "x64"
  617 + ],
  618 + "dev": true,
  619 + "license": "MIT",
  620 + "optional": true,
  621 + "os": [
  622 + "netbsd"
  623 + ],
  624 + "engines": {
  625 + "node": ">=18"
  626 + }
  627 + },
  628 + "node_modules/@esbuild/openbsd-arm64": {
  629 + "version": "0.25.12",
  630 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
  631 + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
  632 + "cpu": [
  633 + "arm64"
  634 + ],
  635 + "dev": true,
  636 + "license": "MIT",
  637 + "optional": true,
  638 + "os": [
  639 + "openbsd"
  640 + ],
  641 + "engines": {
  642 + "node": ">=18"
  643 + }
  644 + },
  645 + "node_modules/@esbuild/openbsd-x64": {
  646 + "version": "0.25.12",
  647 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
  648 + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
  649 + "cpu": [
  650 + "x64"
  651 + ],
  652 + "dev": true,
  653 + "license": "MIT",
  654 + "optional": true,
  655 + "os": [
  656 + "openbsd"
  657 + ],
  658 + "engines": {
  659 + "node": ">=18"
  660 + }
  661 + },
  662 + "node_modules/@esbuild/openharmony-arm64": {
  663 + "version": "0.25.12",
  664 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
  665 + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
  666 + "cpu": [
  667 + "arm64"
  668 + ],
  669 + "dev": true,
  670 + "license": "MIT",
  671 + "optional": true,
  672 + "os": [
  673 + "openharmony"
  674 + ],
  675 + "engines": {
  676 + "node": ">=18"
  677 + }
  678 + },
  679 + "node_modules/@esbuild/sunos-x64": {
  680 + "version": "0.25.12",
  681 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
  682 + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
  683 + "cpu": [
  684 + "x64"
  685 + ],
  686 + "dev": true,
  687 + "license": "MIT",
  688 + "optional": true,
  689 + "os": [
  690 + "sunos"
  691 + ],
  692 + "engines": {
  693 + "node": ">=18"
  694 + }
  695 + },
  696 + "node_modules/@esbuild/win32-arm64": {
  697 + "version": "0.25.12",
  698 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
  699 + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
  700 + "cpu": [
  701 + "arm64"
  702 + ],
  703 + "dev": true,
  704 + "license": "MIT",
  705 + "optional": true,
  706 + "os": [
  707 + "win32"
  708 + ],
  709 + "engines": {
  710 + "node": ">=18"
  711 + }
  712 + },
  713 + "node_modules/@esbuild/win32-ia32": {
  714 + "version": "0.25.12",
  715 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
  716 + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
  717 + "cpu": [
  718 + "ia32"
  719 + ],
  720 + "dev": true,
  721 + "license": "MIT",
  722 + "optional": true,
  723 + "os": [
  724 + "win32"
  725 + ],
  726 + "engines": {
  727 + "node": ">=18"
  728 + }
  729 + },
  730 + "node_modules/@esbuild/win32-x64": {
  731 + "version": "0.25.12",
  732 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
  733 + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
  734 + "cpu": [
  735 + "x64"
  736 + ],
  737 + "dev": true,
  738 + "license": "MIT",
  739 + "optional": true,
  740 + "os": [
  741 + "win32"
  742 + ],
  743 + "engines": {
  744 + "node": ">=18"
  745 + }
  746 + },
  747 + "node_modules/@jridgewell/gen-mapping": {
  748 + "version": "0.3.13",
  749 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
  750 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
  751 + "dev": true,
  752 + "license": "MIT",
  753 + "dependencies": {
  754 + "@jridgewell/sourcemap-codec": "^1.5.0",
  755 + "@jridgewell/trace-mapping": "^0.3.24"
  756 + }
  757 + },
  758 + "node_modules/@jridgewell/remapping": {
  759 + "version": "2.3.5",
  760 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
  761 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
  762 + "dev": true,
  763 + "license": "MIT",
  764 + "dependencies": {
  765 + "@jridgewell/gen-mapping": "^0.3.5",
  766 + "@jridgewell/trace-mapping": "^0.3.24"
  767 + }
  768 + },
  769 + "node_modules/@jridgewell/resolve-uri": {
  770 + "version": "3.1.2",
  771 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
  772 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
  773 + "dev": true,
  774 + "license": "MIT",
  775 + "engines": {
  776 + "node": ">=6.0.0"
  777 + }
  778 + },
  779 + "node_modules/@jridgewell/sourcemap-codec": {
  780 + "version": "1.5.5",
  781 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
  782 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
  783 + "dev": true,
  784 + "license": "MIT"
  785 + },
  786 + "node_modules/@jridgewell/trace-mapping": {
  787 + "version": "0.3.31",
  788 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
  789 + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
  790 + "dev": true,
  791 + "license": "MIT",
  792 + "dependencies": {
  793 + "@jridgewell/resolve-uri": "^3.1.0",
  794 + "@jridgewell/sourcemap-codec": "^1.4.14"
  795 + }
  796 + },
  797 + "node_modules/@rolldown/pluginutils": {
  798 + "version": "1.0.0-beta.53",
  799 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
  800 + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
  801 + "dev": true,
  802 + "license": "MIT"
  803 + },
  804 + "node_modules/@rollup/rollup-android-arm-eabi": {
  805 + "version": "4.54.0",
  806 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
  807 + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
  808 + "cpu": [
  809 + "arm"
  810 + ],
  811 + "dev": true,
  812 + "license": "MIT",
  813 + "optional": true,
  814 + "os": [
  815 + "android"
  816 + ]
  817 + },
  818 + "node_modules/@rollup/rollup-android-arm64": {
  819 + "version": "4.54.0",
  820 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
  821 + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
  822 + "cpu": [
  823 + "arm64"
  824 + ],
  825 + "dev": true,
  826 + "license": "MIT",
  827 + "optional": true,
  828 + "os": [
  829 + "android"
  830 + ]
  831 + },
  832 + "node_modules/@rollup/rollup-darwin-arm64": {
  833 + "version": "4.54.0",
  834 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
  835 + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
  836 + "cpu": [
  837 + "arm64"
  838 + ],
  839 + "dev": true,
  840 + "license": "MIT",
  841 + "optional": true,
  842 + "os": [
  843 + "darwin"
  844 + ]
  845 + },
  846 + "node_modules/@rollup/rollup-darwin-x64": {
  847 + "version": "4.54.0",
  848 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
  849 + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
  850 + "cpu": [
  851 + "x64"
  852 + ],
  853 + "dev": true,
  854 + "license": "MIT",
  855 + "optional": true,
  856 + "os": [
  857 + "darwin"
  858 + ]
  859 + },
  860 + "node_modules/@rollup/rollup-freebsd-arm64": {
  861 + "version": "4.54.0",
  862 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
  863 + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
  864 + "cpu": [
  865 + "arm64"
  866 + ],
  867 + "dev": true,
  868 + "license": "MIT",
  869 + "optional": true,
  870 + "os": [
  871 + "freebsd"
  872 + ]
  873 + },
  874 + "node_modules/@rollup/rollup-freebsd-x64": {
  875 + "version": "4.54.0",
  876 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
  877 + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
  878 + "cpu": [
  879 + "x64"
  880 + ],
  881 + "dev": true,
  882 + "license": "MIT",
  883 + "optional": true,
  884 + "os": [
  885 + "freebsd"
  886 + ]
  887 + },
  888 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
  889 + "version": "4.54.0",
  890 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
  891 + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
  892 + "cpu": [
  893 + "arm"
  894 + ],
  895 + "dev": true,
  896 + "license": "MIT",
  897 + "optional": true,
  898 + "os": [
  899 + "linux"
  900 + ]
  901 + },
  902 + "node_modules/@rollup/rollup-linux-arm-musleabihf": {
  903 + "version": "4.54.0",
  904 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
  905 + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
  906 + "cpu": [
  907 + "arm"
  908 + ],
  909 + "dev": true,
  910 + "license": "MIT",
  911 + "optional": true,
  912 + "os": [
  913 + "linux"
  914 + ]
  915 + },
  916 + "node_modules/@rollup/rollup-linux-arm64-gnu": {
  917 + "version": "4.54.0",
  918 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
  919 + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
  920 + "cpu": [
  921 + "arm64"
  922 + ],
  923 + "dev": true,
  924 + "license": "MIT",
  925 + "optional": true,
  926 + "os": [
  927 + "linux"
  928 + ]
  929 + },
  930 + "node_modules/@rollup/rollup-linux-arm64-musl": {
  931 + "version": "4.54.0",
  932 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
  933 + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
  934 + "cpu": [
  935 + "arm64"
  936 + ],
  937 + "dev": true,
  938 + "license": "MIT",
  939 + "optional": true,
  940 + "os": [
  941 + "linux"
  942 + ]
  943 + },
  944 + "node_modules/@rollup/rollup-linux-loong64-gnu": {
  945 + "version": "4.54.0",
  946 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
  947 + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
  948 + "cpu": [
  949 + "loong64"
  950 + ],
  951 + "dev": true,
  952 + "license": "MIT",
  953 + "optional": true,
  954 + "os": [
  955 + "linux"
  956 + ]
  957 + },
  958 + "node_modules/@rollup/rollup-linux-ppc64-gnu": {
  959 + "version": "4.54.0",
  960 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
  961 + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
  962 + "cpu": [
  963 + "ppc64"
  964 + ],
  965 + "dev": true,
  966 + "license": "MIT",
  967 + "optional": true,
  968 + "os": [
  969 + "linux"
  970 + ]
  971 + },
  972 + "node_modules/@rollup/rollup-linux-riscv64-gnu": {
  973 + "version": "4.54.0",
  974 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
  975 + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
  976 + "cpu": [
  977 + "riscv64"
  978 + ],
  979 + "dev": true,
  980 + "license": "MIT",
  981 + "optional": true,
  982 + "os": [
  983 + "linux"
  984 + ]
  985 + },
  986 + "node_modules/@rollup/rollup-linux-riscv64-musl": {
  987 + "version": "4.54.0",
  988 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
  989 + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
  990 + "cpu": [
  991 + "riscv64"
  992 + ],
  993 + "dev": true,
  994 + "license": "MIT",
  995 + "optional": true,
  996 + "os": [
  997 + "linux"
  998 + ]
  999 + },
  1000 + "node_modules/@rollup/rollup-linux-s390x-gnu": {
  1001 + "version": "4.54.0",
  1002 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
  1003 + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
  1004 + "cpu": [
  1005 + "s390x"
  1006 + ],
  1007 + "dev": true,
  1008 + "license": "MIT",
  1009 + "optional": true,
  1010 + "os": [
  1011 + "linux"
  1012 + ]
  1013 + },
  1014 + "node_modules/@rollup/rollup-linux-x64-gnu": {
  1015 + "version": "4.54.0",
  1016 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
  1017 + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
  1018 + "cpu": [
  1019 + "x64"
  1020 + ],
  1021 + "dev": true,
  1022 + "license": "MIT",
  1023 + "optional": true,
  1024 + "os": [
  1025 + "linux"
  1026 + ]
  1027 + },
  1028 + "node_modules/@rollup/rollup-linux-x64-musl": {
  1029 + "version": "4.54.0",
  1030 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
  1031 + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
  1032 + "cpu": [
  1033 + "x64"
  1034 + ],
  1035 + "dev": true,
  1036 + "license": "MIT",
  1037 + "optional": true,
  1038 + "os": [
  1039 + "linux"
  1040 + ]
  1041 + },
  1042 + "node_modules/@rollup/rollup-openharmony-arm64": {
  1043 + "version": "4.54.0",
  1044 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
  1045 + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
  1046 + "cpu": [
  1047 + "arm64"
  1048 + ],
  1049 + "dev": true,
  1050 + "license": "MIT",
  1051 + "optional": true,
  1052 + "os": [
  1053 + "openharmony"
  1054 + ]
  1055 + },
  1056 + "node_modules/@rollup/rollup-win32-arm64-msvc": {
  1057 + "version": "4.54.0",
  1058 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
  1059 + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
  1060 + "cpu": [
  1061 + "arm64"
  1062 + ],
  1063 + "dev": true,
  1064 + "license": "MIT",
  1065 + "optional": true,
  1066 + "os": [
  1067 + "win32"
  1068 + ]
  1069 + },
  1070 + "node_modules/@rollup/rollup-win32-ia32-msvc": {
  1071 + "version": "4.54.0",
  1072 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
  1073 + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
  1074 + "cpu": [
  1075 + "ia32"
  1076 + ],
  1077 + "dev": true,
  1078 + "license": "MIT",
  1079 + "optional": true,
  1080 + "os": [
  1081 + "win32"
  1082 + ]
  1083 + },
  1084 + "node_modules/@rollup/rollup-win32-x64-gnu": {
  1085 + "version": "4.54.0",
  1086 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
  1087 + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
  1088 + "cpu": [
  1089 + "x64"
  1090 + ],
  1091 + "dev": true,
  1092 + "license": "MIT",
  1093 + "optional": true,
  1094 + "os": [
  1095 + "win32"
  1096 + ]
  1097 + },
  1098 + "node_modules/@rollup/rollup-win32-x64-msvc": {
  1099 + "version": "4.54.0",
  1100 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
  1101 + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
  1102 + "cpu": [
  1103 + "x64"
  1104 + ],
  1105 + "dev": true,
  1106 + "license": "MIT",
  1107 + "optional": true,
  1108 + "os": [
  1109 + "win32"
  1110 + ]
  1111 + },
  1112 + "node_modules/@types/babel__core": {
  1113 + "version": "7.20.5",
  1114 + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
  1115 + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
  1116 + "dev": true,
  1117 + "license": "MIT",
  1118 + "dependencies": {
  1119 + "@babel/parser": "^7.20.7",
  1120 + "@babel/types": "^7.20.7",
  1121 + "@types/babel__generator": "*",
  1122 + "@types/babel__template": "*",
  1123 + "@types/babel__traverse": "*"
  1124 + }
  1125 + },
  1126 + "node_modules/@types/babel__generator": {
  1127 + "version": "7.27.0",
  1128 + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
  1129 + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
  1130 + "dev": true,
  1131 + "license": "MIT",
  1132 + "dependencies": {
  1133 + "@babel/types": "^7.0.0"
  1134 + }
  1135 + },
  1136 + "node_modules/@types/babel__template": {
  1137 + "version": "7.4.4",
  1138 + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
  1139 + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
  1140 + "dev": true,
  1141 + "license": "MIT",
  1142 + "dependencies": {
  1143 + "@babel/parser": "^7.1.0",
  1144 + "@babel/types": "^7.0.0"
  1145 + }
  1146 + },
  1147 + "node_modules/@types/babel__traverse": {
  1148 + "version": "7.28.0",
  1149 + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
  1150 + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
  1151 + "dev": true,
  1152 + "license": "MIT",
  1153 + "dependencies": {
  1154 + "@babel/types": "^7.28.2"
  1155 + }
  1156 + },
  1157 + "node_modules/@types/estree": {
  1158 + "version": "1.0.8",
  1159 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
  1160 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
  1161 + "dev": true,
  1162 + "license": "MIT"
  1163 + },
  1164 + "node_modules/@types/node": {
  1165 + "version": "22.19.3",
  1166 + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
  1167 + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
  1168 + "dev": true,
  1169 + "license": "MIT",
  1170 + "peer": true,
  1171 + "dependencies": {
  1172 + "undici-types": "~6.21.0"
  1173 + }
  1174 + },
  1175 + "node_modules/@vitejs/plugin-react": {
  1176 + "version": "5.1.2",
  1177 + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
  1178 + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
  1179 + "dev": true,
  1180 + "license": "MIT",
  1181 + "dependencies": {
  1182 + "@babel/core": "^7.28.5",
  1183 + "@babel/plugin-transform-react-jsx-self": "^7.27.1",
  1184 + "@babel/plugin-transform-react-jsx-source": "^7.27.1",
  1185 + "@rolldown/pluginutils": "1.0.0-beta.53",
  1186 + "@types/babel__core": "^7.20.5",
  1187 + "react-refresh": "^0.18.0"
  1188 + },
  1189 + "engines": {
  1190 + "node": "^20.19.0 || >=22.12.0"
  1191 + },
  1192 + "peerDependencies": {
  1193 + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
  1194 + }
  1195 + },
  1196 + "node_modules/baseline-browser-mapping": {
  1197 + "version": "2.9.11",
  1198 + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
  1199 + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
  1200 + "dev": true,
  1201 + "license": "Apache-2.0",
  1202 + "bin": {
  1203 + "baseline-browser-mapping": "dist/cli.js"
  1204 + }
  1205 + },
  1206 + "node_modules/browserslist": {
  1207 + "version": "4.28.1",
  1208 + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
  1209 + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
  1210 + "dev": true,
  1211 + "funding": [
  1212 + {
  1213 + "type": "opencollective",
  1214 + "url": "https://opencollective.com/browserslist"
  1215 + },
  1216 + {
  1217 + "type": "tidelift",
  1218 + "url": "https://tidelift.com/funding/github/npm/browserslist"
  1219 + },
  1220 + {
  1221 + "type": "github",
  1222 + "url": "https://github.com/sponsors/ai"
  1223 + }
  1224 + ],
  1225 + "license": "MIT",
  1226 + "peer": true,
  1227 + "dependencies": {
  1228 + "baseline-browser-mapping": "^2.9.0",
  1229 + "caniuse-lite": "^1.0.30001759",
  1230 + "electron-to-chromium": "^1.5.263",
  1231 + "node-releases": "^2.0.27",
  1232 + "update-browserslist-db": "^1.2.0"
  1233 + },
  1234 + "bin": {
  1235 + "browserslist": "cli.js"
  1236 + },
  1237 + "engines": {
  1238 + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
  1239 + }
  1240 + },
  1241 + "node_modules/caniuse-lite": {
  1242 + "version": "1.0.30001761",
  1243 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
  1244 + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
  1245 + "dev": true,
  1246 + "funding": [
  1247 + {
  1248 + "type": "opencollective",
  1249 + "url": "https://opencollective.com/browserslist"
  1250 + },
  1251 + {
  1252 + "type": "tidelift",
  1253 + "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
  1254 + },
  1255 + {
  1256 + "type": "github",
  1257 + "url": "https://github.com/sponsors/ai"
  1258 + }
  1259 + ],
  1260 + "license": "CC-BY-4.0"
  1261 + },
  1262 + "node_modules/convert-source-map": {
  1263 + "version": "2.0.0",
  1264 + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
  1265 + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
  1266 + "dev": true,
  1267 + "license": "MIT"
  1268 + },
  1269 + "node_modules/debug": {
  1270 + "version": "4.4.3",
  1271 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
  1272 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
  1273 + "dev": true,
  1274 + "license": "MIT",
  1275 + "dependencies": {
  1276 + "ms": "^2.1.3"
  1277 + },
  1278 + "engines": {
  1279 + "node": ">=6.0"
  1280 + },
  1281 + "peerDependenciesMeta": {
  1282 + "supports-color": {
  1283 + "optional": true
  1284 + }
  1285 + }
  1286 + },
  1287 + "node_modules/electron-to-chromium": {
  1288 + "version": "1.5.267",
  1289 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
  1290 + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
  1291 + "dev": true,
  1292 + "license": "ISC"
  1293 + },
  1294 + "node_modules/esbuild": {
  1295 + "version": "0.25.12",
  1296 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
  1297 + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
  1298 + "dev": true,
  1299 + "hasInstallScript": true,
  1300 + "license": "MIT",
  1301 + "bin": {
  1302 + "esbuild": "bin/esbuild"
  1303 + },
  1304 + "engines": {
  1305 + "node": ">=18"
  1306 + },
  1307 + "optionalDependencies": {
  1308 + "@esbuild/aix-ppc64": "0.25.12",
  1309 + "@esbuild/android-arm": "0.25.12",
  1310 + "@esbuild/android-arm64": "0.25.12",
  1311 + "@esbuild/android-x64": "0.25.12",
  1312 + "@esbuild/darwin-arm64": "0.25.12",
  1313 + "@esbuild/darwin-x64": "0.25.12",
  1314 + "@esbuild/freebsd-arm64": "0.25.12",
  1315 + "@esbuild/freebsd-x64": "0.25.12",
  1316 + "@esbuild/linux-arm": "0.25.12",
  1317 + "@esbuild/linux-arm64": "0.25.12",
  1318 + "@esbuild/linux-ia32": "0.25.12",
  1319 + "@esbuild/linux-loong64": "0.25.12",
  1320 + "@esbuild/linux-mips64el": "0.25.12",
  1321 + "@esbuild/linux-ppc64": "0.25.12",
  1322 + "@esbuild/linux-riscv64": "0.25.12",
  1323 + "@esbuild/linux-s390x": "0.25.12",
  1324 + "@esbuild/linux-x64": "0.25.12",
  1325 + "@esbuild/netbsd-arm64": "0.25.12",
  1326 + "@esbuild/netbsd-x64": "0.25.12",
  1327 + "@esbuild/openbsd-arm64": "0.25.12",
  1328 + "@esbuild/openbsd-x64": "0.25.12",
  1329 + "@esbuild/openharmony-arm64": "0.25.12",
  1330 + "@esbuild/sunos-x64": "0.25.12",
  1331 + "@esbuild/win32-arm64": "0.25.12",
  1332 + "@esbuild/win32-ia32": "0.25.12",
  1333 + "@esbuild/win32-x64": "0.25.12"
  1334 + }
  1335 + },
  1336 + "node_modules/escalade": {
  1337 + "version": "3.2.0",
  1338 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
  1339 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
  1340 + "dev": true,
  1341 + "license": "MIT",
  1342 + "engines": {
  1343 + "node": ">=6"
  1344 + }
  1345 + },
  1346 + "node_modules/fdir": {
  1347 + "version": "6.5.0",
  1348 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
  1349 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
  1350 + "dev": true,
  1351 + "license": "MIT",
  1352 + "engines": {
  1353 + "node": ">=12.0.0"
  1354 + },
  1355 + "peerDependencies": {
  1356 + "picomatch": "^3 || ^4"
  1357 + },
  1358 + "peerDependenciesMeta": {
  1359 + "picomatch": {
  1360 + "optional": true
  1361 + }
  1362 + }
  1363 + },
  1364 + "node_modules/fsevents": {
  1365 + "version": "2.3.3",
  1366 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
  1367 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
  1368 + "dev": true,
  1369 + "hasInstallScript": true,
  1370 + "license": "MIT",
  1371 + "optional": true,
  1372 + "os": [
  1373 + "darwin"
  1374 + ],
  1375 + "engines": {
  1376 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
  1377 + }
  1378 + },
  1379 + "node_modules/gensync": {
  1380 + "version": "1.0.0-beta.2",
  1381 + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
  1382 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
  1383 + "dev": true,
  1384 + "license": "MIT",
  1385 + "engines": {
  1386 + "node": ">=6.9.0"
  1387 + }
  1388 + },
  1389 + "node_modules/js-tokens": {
  1390 + "version": "4.0.0",
  1391 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
  1392 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
  1393 + "dev": true,
  1394 + "license": "MIT"
  1395 + },
  1396 + "node_modules/jsesc": {
  1397 + "version": "3.1.0",
  1398 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
  1399 + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
  1400 + "dev": true,
  1401 + "license": "MIT",
  1402 + "bin": {
  1403 + "jsesc": "bin/jsesc"
  1404 + },
  1405 + "engines": {
  1406 + "node": ">=6"
  1407 + }
  1408 + },
  1409 + "node_modules/json5": {
  1410 + "version": "2.2.3",
  1411 + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
  1412 + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
  1413 + "dev": true,
  1414 + "license": "MIT",
  1415 + "bin": {
  1416 + "json5": "lib/cli.js"
  1417 + },
  1418 + "engines": {
  1419 + "node": ">=6"
  1420 + }
  1421 + },
  1422 + "node_modules/lru-cache": {
  1423 + "version": "5.1.1",
  1424 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
  1425 + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
  1426 + "dev": true,
  1427 + "license": "ISC",
  1428 + "dependencies": {
  1429 + "yallist": "^3.0.2"
  1430 + }
  1431 + },
  1432 + "node_modules/lucide-react": {
  1433 + "version": "0.561.0",
  1434 + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.561.0.tgz",
  1435 + "integrity": "sha512-Y59gMY38tl4/i0qewcqohPdEbieBy7SovpBL9IFebhc2mDd8x4PZSOsiFRkpPcOq6bj1r/mjH/Rk73gSlIJP2A==",
  1436 + "license": "ISC",
  1437 + "peerDependencies": {
  1438 + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
  1439 + }
  1440 + },
  1441 + "node_modules/ms": {
  1442 + "version": "2.1.3",
  1443 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
  1444 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
  1445 + "dev": true,
  1446 + "license": "MIT"
  1447 + },
  1448 + "node_modules/nanoid": {
  1449 + "version": "3.3.11",
  1450 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
  1451 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
  1452 + "dev": true,
  1453 + "funding": [
  1454 + {
  1455 + "type": "github",
  1456 + "url": "https://github.com/sponsors/ai"
  1457 + }
  1458 + ],
  1459 + "license": "MIT",
  1460 + "bin": {
  1461 + "nanoid": "bin/nanoid.cjs"
  1462 + },
  1463 + "engines": {
  1464 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
  1465 + }
  1466 + },
  1467 + "node_modules/node-releases": {
  1468 + "version": "2.0.27",
  1469 + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
  1470 + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
  1471 + "dev": true,
  1472 + "license": "MIT"
  1473 + },
  1474 + "node_modules/picocolors": {
  1475 + "version": "1.1.1",
  1476 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
  1477 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
  1478 + "dev": true,
  1479 + "license": "ISC"
  1480 + },
  1481 + "node_modules/picomatch": {
  1482 + "version": "4.0.3",
  1483 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
  1484 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
  1485 + "dev": true,
  1486 + "license": "MIT",
  1487 + "peer": true,
  1488 + "engines": {
  1489 + "node": ">=12"
  1490 + },
  1491 + "funding": {
  1492 + "url": "https://github.com/sponsors/jonschlinkert"
  1493 + }
  1494 + },
  1495 + "node_modules/postcss": {
  1496 + "version": "8.5.6",
  1497 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
  1498 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
  1499 + "dev": true,
  1500 + "funding": [
  1501 + {
  1502 + "type": "opencollective",
  1503 + "url": "https://opencollective.com/postcss/"
  1504 + },
  1505 + {
  1506 + "type": "tidelift",
  1507 + "url": "https://tidelift.com/funding/github/npm/postcss"
  1508 + },
  1509 + {
  1510 + "type": "github",
  1511 + "url": "https://github.com/sponsors/ai"
  1512 + }
  1513 + ],
  1514 + "license": "MIT",
  1515 + "dependencies": {
  1516 + "nanoid": "^3.3.11",
  1517 + "picocolors": "^1.1.1",
  1518 + "source-map-js": "^1.2.1"
  1519 + },
  1520 + "engines": {
  1521 + "node": "^10 || ^12 || >=14"
  1522 + }
  1523 + },
  1524 + "node_modules/react": {
  1525 + "version": "19.2.3",
  1526 + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
  1527 + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
  1528 + "license": "MIT",
  1529 + "peer": true,
  1530 + "engines": {
  1531 + "node": ">=0.10.0"
  1532 + }
  1533 + },
  1534 + "node_modules/react-dom": {
  1535 + "version": "19.2.3",
  1536 + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
  1537 + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
  1538 + "license": "MIT",
  1539 + "dependencies": {
  1540 + "scheduler": "^0.27.0"
  1541 + },
  1542 + "peerDependencies": {
  1543 + "react": "^19.2.3"
  1544 + }
  1545 + },
  1546 + "node_modules/react-refresh": {
  1547 + "version": "0.18.0",
  1548 + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
  1549 + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
  1550 + "dev": true,
  1551 + "license": "MIT",
  1552 + "engines": {
  1553 + "node": ">=0.10.0"
  1554 + }
  1555 + },
  1556 + "node_modules/rollup": {
  1557 + "version": "4.54.0",
  1558 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
  1559 + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
  1560 + "dev": true,
  1561 + "license": "MIT",
  1562 + "dependencies": {
  1563 + "@types/estree": "1.0.8"
  1564 + },
  1565 + "bin": {
  1566 + "rollup": "dist/bin/rollup"
  1567 + },
  1568 + "engines": {
  1569 + "node": ">=18.0.0",
  1570 + "npm": ">=8.0.0"
  1571 + },
  1572 + "optionalDependencies": {
  1573 + "@rollup/rollup-android-arm-eabi": "4.54.0",
  1574 + "@rollup/rollup-android-arm64": "4.54.0",
  1575 + "@rollup/rollup-darwin-arm64": "4.54.0",
  1576 + "@rollup/rollup-darwin-x64": "4.54.0",
  1577 + "@rollup/rollup-freebsd-arm64": "4.54.0",
  1578 + "@rollup/rollup-freebsd-x64": "4.54.0",
  1579 + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
  1580 + "@rollup/rollup-linux-arm-musleabihf": "4.54.0",
  1581 + "@rollup/rollup-linux-arm64-gnu": "4.54.0",
  1582 + "@rollup/rollup-linux-arm64-musl": "4.54.0",
  1583 + "@rollup/rollup-linux-loong64-gnu": "4.54.0",
  1584 + "@rollup/rollup-linux-ppc64-gnu": "4.54.0",
  1585 + "@rollup/rollup-linux-riscv64-gnu": "4.54.0",
  1586 + "@rollup/rollup-linux-riscv64-musl": "4.54.0",
  1587 + "@rollup/rollup-linux-s390x-gnu": "4.54.0",
  1588 + "@rollup/rollup-linux-x64-gnu": "4.54.0",
  1589 + "@rollup/rollup-linux-x64-musl": "4.54.0",
  1590 + "@rollup/rollup-openharmony-arm64": "4.54.0",
  1591 + "@rollup/rollup-win32-arm64-msvc": "4.54.0",
  1592 + "@rollup/rollup-win32-ia32-msvc": "4.54.0",
  1593 + "@rollup/rollup-win32-x64-gnu": "4.54.0",
  1594 + "@rollup/rollup-win32-x64-msvc": "4.54.0",
  1595 + "fsevents": "~2.3.2"
  1596 + }
  1597 + },
  1598 + "node_modules/scheduler": {
  1599 + "version": "0.27.0",
  1600 + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
  1601 + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
  1602 + "license": "MIT"
  1603 + },
  1604 + "node_modules/semver": {
  1605 + "version": "6.3.1",
  1606 + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
  1607 + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
  1608 + "dev": true,
  1609 + "license": "ISC",
  1610 + "bin": {
  1611 + "semver": "bin/semver.js"
  1612 + }
  1613 + },
  1614 + "node_modules/source-map-js": {
  1615 + "version": "1.2.1",
  1616 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
  1617 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
  1618 + "dev": true,
  1619 + "license": "BSD-3-Clause",
  1620 + "engines": {
  1621 + "node": ">=0.10.0"
  1622 + }
  1623 + },
  1624 + "node_modules/tinyglobby": {
  1625 + "version": "0.2.15",
  1626 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
  1627 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
  1628 + "dev": true,
  1629 + "license": "MIT",
  1630 + "dependencies": {
  1631 + "fdir": "^6.5.0",
  1632 + "picomatch": "^4.0.3"
  1633 + },
  1634 + "engines": {
  1635 + "node": ">=12.0.0"
  1636 + },
  1637 + "funding": {
  1638 + "url": "https://github.com/sponsors/SuperchupuDev"
  1639 + }
  1640 + },
  1641 + "node_modules/typescript": {
  1642 + "version": "5.8.3",
  1643 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
  1644 + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
  1645 + "dev": true,
  1646 + "license": "Apache-2.0",
  1647 + "bin": {
  1648 + "tsc": "bin/tsc",
  1649 + "tsserver": "bin/tsserver"
  1650 + },
  1651 + "engines": {
  1652 + "node": ">=14.17"
  1653 + }
  1654 + },
  1655 + "node_modules/undici-types": {
  1656 + "version": "6.21.0",
  1657 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
  1658 + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
  1659 + "dev": true,
  1660 + "license": "MIT"
  1661 + },
  1662 + "node_modules/update-browserslist-db": {
  1663 + "version": "1.2.3",
  1664 + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
  1665 + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
  1666 + "dev": true,
  1667 + "funding": [
  1668 + {
  1669 + "type": "opencollective",
  1670 + "url": "https://opencollective.com/browserslist"
  1671 + },
  1672 + {
  1673 + "type": "tidelift",
  1674 + "url": "https://tidelift.com/funding/github/npm/browserslist"
  1675 + },
  1676 + {
  1677 + "type": "github",
  1678 + "url": "https://github.com/sponsors/ai"
  1679 + }
  1680 + ],
  1681 + "license": "MIT",
  1682 + "dependencies": {
  1683 + "escalade": "^3.2.0",
  1684 + "picocolors": "^1.1.1"
  1685 + },
  1686 + "bin": {
  1687 + "update-browserslist-db": "cli.js"
  1688 + },
  1689 + "peerDependencies": {
  1690 + "browserslist": ">= 4.21.0"
  1691 + }
  1692 + },
  1693 + "node_modules/vite": {
  1694 + "version": "6.4.1",
  1695 + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
  1696 + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
  1697 + "dev": true,
  1698 + "license": "MIT",
  1699 + "peer": true,
  1700 + "dependencies": {
  1701 + "esbuild": "^0.25.0",
  1702 + "fdir": "^6.4.4",
  1703 + "picomatch": "^4.0.2",
  1704 + "postcss": "^8.5.3",
  1705 + "rollup": "^4.34.9",
  1706 + "tinyglobby": "^0.2.13"
  1707 + },
  1708 + "bin": {
  1709 + "vite": "bin/vite.js"
  1710 + },
  1711 + "engines": {
  1712 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
  1713 + },
  1714 + "funding": {
  1715 + "url": "https://github.com/vitejs/vite?sponsor=1"
  1716 + },
  1717 + "optionalDependencies": {
  1718 + "fsevents": "~2.3.3"
  1719 + },
  1720 + "peerDependencies": {
  1721 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
  1722 + "jiti": ">=1.21.0",
  1723 + "less": "*",
  1724 + "lightningcss": "^1.21.0",
  1725 + "sass": "*",
  1726 + "sass-embedded": "*",
  1727 + "stylus": "*",
  1728 + "sugarss": "*",
  1729 + "terser": "^5.16.0",
  1730 + "tsx": "^4.8.1",
  1731 + "yaml": "^2.4.2"
  1732 + },
  1733 + "peerDependenciesMeta": {
  1734 + "@types/node": {
  1735 + "optional": true
  1736 + },
  1737 + "jiti": {
  1738 + "optional": true
  1739 + },
  1740 + "less": {
  1741 + "optional": true
  1742 + },
  1743 + "lightningcss": {
  1744 + "optional": true
  1745 + },
  1746 + "sass": {
  1747 + "optional": true
  1748 + },
  1749 + "sass-embedded": {
  1750 + "optional": true
  1751 + },
  1752 + "stylus": {
  1753 + "optional": true
  1754 + },
  1755 + "sugarss": {
  1756 + "optional": true
  1757 + },
  1758 + "terser": {
  1759 + "optional": true
  1760 + },
  1761 + "tsx": {
  1762 + "optional": true
  1763 + },
  1764 + "yaml": {
  1765 + "optional": true
  1766 + }
  1767 + }
  1768 + },
  1769 + "node_modules/yallist": {
  1770 + "version": "3.1.1",
  1771 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
  1772 + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
  1773 + "dev": true,
  1774 + "license": "ISC"
  1775 + }
  1776 + }
  1777 +}
  1 +import { API_BASE_URL } from '../constants';
  2 +
  3 +export const login = async (userId: string): Promise<boolean> => {
  4 + try {
  5 + const response = await fetch(`${API_BASE_URL}/auth/login?userId=${userId}`, {
  6 + method: 'POST',
  7 + });
  8 + if (response.status === 200) {
  9 + return true;
  10 + }
  11 + return false;
  12 + } catch (error) {
  13 + console.error("Login error:", error);
  14 + return false;
  15 + }
  16 +};
  17 +
  18 +export const getWhitelist = async (): Promise<string[]> => {
  19 + const response = await fetch(`${API_BASE_URL}/admin/whitelist`);
  20 + if (!response.ok) throw new Error('Failed to fetch whitelist');
  21 + const data = await response.json();
  22 + return data.whitelist;
  23 +};
  24 +
  25 +export const addWhitelist = async (userIds: string[]): Promise<string[]> => {
  26 + const response = await fetch(`${API_BASE_URL}/admin/whitelist`, {
  27 + method: 'POST',
  28 + headers: { 'Content-Type': 'application/json' },
  29 + body: JSON.stringify(userIds),
  30 + });
  31 + if (!response.ok) throw new Error('Failed to add to whitelist');
  32 + const data = await response.json();
  33 + return data.whitelist;
  34 +};
  35 +
  36 +export const removeWhitelist = async (userId: string): Promise<string[]> => {
  37 + const response = await fetch(`${API_BASE_URL}/admin/whitelist/${userId}`, {
  38 + method: 'DELETE',
  39 + });
  40 + if (!response.ok) throw new Error('Failed to remove from whitelist');
  41 + const data = await response.json();
  42 + return data.whitelist;
  43 +};