diff --git a/.gitignore b/.gitignore
index 36a2bbd..1bb3bc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,7 @@
.pnp.js
# production
-/dist
+dist
# env files
.env
diff --git a/Dockerfile b/Dockerfile
index 1bf5a94..97947e0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN pnpm install @amap/amap-maps-mcp-server @playwright/mcp@latest tavily-mcp@la
COPY . .
-RUN pnpm build
+RUN pnpm frontend:build && pnpm build
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
diff --git a/public/favicon.ico b/frontend/favicon.ico
similarity index 100%
rename from public/favicon.ico
rename to frontend/favicon.ico
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..8449b96
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ MCP Hub Dashboard
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js
new file mode 100644
index 0000000..51a6e4e
--- /dev/null
+++ b/frontend/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ autoprefixer: {},
+ },
+};
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
new file mode 100644
index 0000000..680b66a
--- /dev/null
+++ b/frontend/src/App.tsx
@@ -0,0 +1,156 @@
+import { useState, useEffect } from 'react'
+import { Server, ApiResponse } from './types'
+import ServerCard from './components/ServerCard'
+import AddServerForm from './components/AddServerForm'
+import EditServerForm from './components/EditServerForm'
+
+function App() {
+ const [servers, setServers] = useState([])
+ const [error, setError] = useState(null)
+ const [refreshKey, setRefreshKey] = useState(0)
+ const [editingServer, setEditingServer] = useState(null)
+
+ useEffect(() => {
+ const fetchServers = async () => {
+ try {
+ const response = await fetch('/api/servers')
+ const data = await response.json()
+
+ // 处理API响应中的包装对象,提取data字段
+ if (data && data.success && Array.isArray(data.data)) {
+ setServers(data.data)
+ } else if (data && Array.isArray(data)) {
+ // 兼容性处理,如果API直接返回数组
+ setServers(data)
+ } else {
+ // 如果数据格式不符合预期,设置为空数组
+ console.error('Invalid server data format:', data)
+ setServers([])
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : String(err))
+ }
+ }
+
+ fetchServers()
+
+ // Poll for updates every 5 seconds
+ const interval = setInterval(fetchServers, 5000)
+ return () => clearInterval(interval)
+ }, [refreshKey])
+
+ const handleServerAdd = () => {
+ setRefreshKey(prevKey => prevKey + 1)
+ }
+
+ const handleServerEdit = (server: Server) => {
+ // Fetch settings to get the full server config before editing
+ fetch(`/api/settings`)
+ .then(response => response.json())
+ .then((settingsData: ApiResponse<{ mcpServers: Record }>) => {
+ if (
+ settingsData &&
+ settingsData.success &&
+ settingsData.data &&
+ settingsData.data.mcpServers &&
+ settingsData.data.mcpServers[server.name]
+ ) {
+ const serverConfig = settingsData.data.mcpServers[server.name]
+ const fullServerData = {
+ name: server.name,
+ status: server.status,
+ tools: server.tools || [],
+ config: serverConfig,
+ }
+
+ console.log('Editing server with config:', fullServerData)
+ setEditingServer(fullServerData)
+ } else {
+ console.error('Failed to get server config from settings:', settingsData)
+ setError(`Could not find configuration data for ${server.name}`)
+ }
+ })
+ .catch(err => {
+ console.error('Error fetching server settings:', err)
+ setError(err instanceof Error ? err.message : String(err))
+ })
+ }
+
+ const handleEditComplete = () => {
+ setEditingServer(null)
+ setRefreshKey(prevKey => prevKey + 1)
+ }
+
+ const handleServerRemove = async (serverName: string) => {
+ try {
+ const response = await fetch(`/api/servers/${serverName}`, {
+ method: 'DELETE',
+ })
+ const result = await response.json()
+
+ if (!response.ok) {
+ setError(result.message || `Failed to delete server ${serverName}`)
+ return
+ }
+
+ setRefreshKey(prevKey => prevKey + 1)
+ } catch (err) {
+ setError('Error: ' + (err instanceof Error ? err.message : String(err)))
+ }
+ }
+
+ if (error) {
+ return (
+
+
+
+
Error
+
{error}
+
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ {servers.length === 0 ? (
+
+
No MCP servers available
+
+ ) : (
+
+ {servers.map((server, index) => (
+
+ ))}
+
+ )}
+ {editingServer && (
+
setEditingServer(null)}
+ />
+ )}
+
+
+ )
+}
+
+export default App
\ No newline at end of file
diff --git a/frontend/src/components/AddServerForm.tsx b/frontend/src/components/AddServerForm.tsx
new file mode 100644
index 0000000..1499cce
--- /dev/null
+++ b/frontend/src/components/AddServerForm.tsx
@@ -0,0 +1,55 @@
+import { useState } from 'react'
+import ServerForm from './ServerForm'
+
+interface AddServerFormProps {
+ onAdd: () => void
+}
+
+const AddServerForm = ({ onAdd }: AddServerFormProps) => {
+ const [modalVisible, setModalVisible] = useState(false)
+
+ const toggleModal = () => {
+ setModalVisible(!modalVisible)
+ }
+
+ const handleSubmit = async (payload: any) => {
+ try {
+ const response = await fetch('/api/servers', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ })
+
+ const result = await response.json()
+
+ if (!response.ok) {
+ alert(result.message || 'Failed to add server')
+ return
+ }
+
+ setModalVisible(false)
+ onAdd()
+ } catch (err) {
+ alert(`Error: ${err instanceof Error ? err.message : String(err)}`)
+ }
+ }
+
+ return (
+
+
+
+ {modalVisible && (
+
+
+
+ )}
+
+ )
+}
+
+export default AddServerForm
\ No newline at end of file
diff --git a/frontend/src/components/EditServerForm.tsx b/frontend/src/components/EditServerForm.tsx
new file mode 100644
index 0000000..5659ed4
--- /dev/null
+++ b/frontend/src/components/EditServerForm.tsx
@@ -0,0 +1,44 @@
+import { Server } from '@/types'
+import ServerForm from './ServerForm'
+
+interface EditServerFormProps {
+ server: Server
+ onEdit: () => void
+ onCancel: () => void
+}
+
+const EditServerForm = ({ server, onEdit, onCancel }: EditServerFormProps) => {
+ const handleSubmit = async (payload: any) => {
+ try {
+ const response = await fetch(`/api/servers/${server.name}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ })
+
+ const result = await response.json()
+
+ if (!response.ok) {
+ alert(result.message || 'Failed to update server')
+ return
+ }
+
+ onEdit()
+ } catch (err) {
+ alert(`Error: ${err instanceof Error ? err.message : String(err)}`)
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+export default EditServerForm
\ No newline at end of file
diff --git a/frontend/src/components/ServerCard.tsx b/frontend/src/components/ServerCard.tsx
new file mode 100644
index 0000000..e8b707e
--- /dev/null
+++ b/frontend/src/components/ServerCard.tsx
@@ -0,0 +1,83 @@
+import { useState } from 'react'
+import { Server } from '@/types'
+import { ChevronDown, ChevronRight } from '@/components/icons/LucideIcons'
+import Badge from '@/components/ui/Badge'
+import ToolCard from '@/components/ui/ToolCard'
+import DeleteDialog from '@/components/ui/DeleteDialog'
+
+interface ServerCardProps {
+ server: Server
+ onRemove: (serverName: string) => void
+ onEdit: (server: Server) => void
+}
+
+const ServerCard = ({ server, onRemove, onEdit }: ServerCardProps) => {
+ const [isExpanded, setIsExpanded] = useState(false)
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false)
+
+ const handleRemove = (e: React.MouseEvent) => {
+ e.stopPropagation()
+ setShowDeleteDialog(true)
+ }
+
+ const handleEdit = (e: React.MouseEvent) => {
+ e.stopPropagation()
+ onEdit(server)
+ }
+
+ const handleConfirmDelete = () => {
+ onRemove(server.name)
+ setShowDeleteDialog(false)
+ }
+
+ return (
+
+
setIsExpanded(!isExpanded)}
+ >
+
+
{server.name}
+
+
+
+
+
+
+
+
+
+
setShowDeleteDialog(false)}
+ onConfirm={handleConfirmDelete}
+ serverName={server.name}
+ />
+
+ {isExpanded && server.tools && (
+
+
Available Tools
+
+ {server.tools.map((tool, index) => (
+
+ ))}
+
+
+ )}
+
+ )
+}
+
+export default ServerCard
\ No newline at end of file
diff --git a/frontend/src/components/ServerForm.tsx b/frontend/src/components/ServerForm.tsx
new file mode 100644
index 0000000..a71bd72
--- /dev/null
+++ b/frontend/src/components/ServerForm.tsx
@@ -0,0 +1,269 @@
+import { useState } from 'react'
+import { Server, EnvVar, ServerFormData } from '@/types'
+
+interface ServerFormProps {
+ onSubmit: (payload: any) => void
+ onCancel: () => void
+ initialData?: Server | null
+ modalTitle: string
+}
+
+const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle }: ServerFormProps) => {
+ const [serverType, setServerType] = useState<'sse' | 'stdio'>(
+ initialData && initialData.config && initialData.config.url ? 'sse' : 'stdio',
+ )
+
+ const [formData, setFormData] = useState({
+ name: (initialData && initialData.name) || '',
+ url: (initialData && initialData.config && initialData.config.url) || '',
+ command: (initialData && initialData.config && initialData.config.command) || '',
+ arguments:
+ initialData && initialData.config && initialData.config.args
+ ? Array.isArray(initialData.config.args)
+ ? initialData.config.args.join(' ')
+ : String(initialData.config.args)
+ : '',
+ args: (initialData && initialData.config && initialData.config.args) || [],
+ })
+
+ const [envVars, setEnvVars] = useState(
+ initialData && initialData.config && initialData.config.env
+ ? Object.entries(initialData.config.env).map(([key, value]) => ({ key, value }))
+ : [],
+ )
+
+ const [error, setError] = useState(null)
+ const isEdit = !!initialData
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target
+ setFormData({ ...formData, [name]: value })
+ }
+
+ // Transform space-separated arguments string into array
+ const handleArgsChange = (value: string) => {
+ let args = value.split(' ').filter((arg) => arg.trim() !== '')
+ setFormData({ ...formData, arguments: value, args })
+ }
+
+ const handleEnvVarChange = (index: number, field: 'key' | 'value', value: string) => {
+ const newEnvVars = [...envVars]
+ newEnvVars[index][field] = value
+ setEnvVars(newEnvVars)
+ }
+
+ const addEnvVar = () => {
+ setEnvVars([...envVars, { key: '', value: '' }])
+ }
+
+ const removeEnvVar = (index: number) => {
+ const newEnvVars = [...envVars]
+ newEnvVars.splice(index, 1)
+ setEnvVars(newEnvVars)
+ }
+
+ // Submit handler for server configuration
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError(null)
+
+ try {
+ const env: Record = {}
+ envVars.forEach(({ key, value }) => {
+ if (key.trim()) {
+ env[key.trim()] = value
+ }
+ })
+
+ const payload = {
+ name: formData.name,
+ config:
+ serverType === 'sse'
+ ? { url: formData.url }
+ : {
+ command: formData.command,
+ args: formData.args,
+ env: Object.keys(env).length > 0 ? env : undefined,
+ },
+ }
+
+ onSubmit(payload)
+ } catch (err) {
+ setError(`Error: ${err instanceof Error ? err.message : String(err)}`)
+ }
+ }
+
+ return (
+
+
+
{modalTitle}
+
+
+
+ {error &&
{error}
}
+
+
+
+ )
+}
+
+export default ServerForm
\ No newline at end of file
diff --git a/frontend/src/components/icons/LucideIcons.tsx b/frontend/src/components/icons/LucideIcons.tsx
new file mode 100644
index 0000000..83759df
--- /dev/null
+++ b/frontend/src/components/icons/LucideIcons.tsx
@@ -0,0 +1,10 @@
+import { ChevronDown, ChevronRight } from 'lucide-react'
+
+export { ChevronDown, ChevronRight }
+
+const LucideIcons = {
+ ChevronDown,
+ ChevronRight,
+}
+
+export default LucideIcons
\ No newline at end of file
diff --git a/frontend/src/components/ui/Badge.tsx b/frontend/src/components/ui/Badge.tsx
new file mode 100644
index 0000000..560b3d5
--- /dev/null
+++ b/frontend/src/components/ui/Badge.tsx
@@ -0,0 +1,23 @@
+import { ServerStatus } from '@/types'
+
+interface BadgeProps {
+ status: ServerStatus
+}
+
+const Badge = ({ status }: BadgeProps) => {
+ const colors = {
+ connecting: 'bg-yellow-100 text-yellow-800',
+ connected: 'bg-green-100 text-green-800',
+ disconnected: 'bg-red-100 text-red-800',
+ }
+
+ return (
+
+ {status}
+
+ )
+}
+
+export default Badge
\ No newline at end of file
diff --git a/frontend/src/components/ui/DeleteDialog.tsx b/frontend/src/components/ui/DeleteDialog.tsx
new file mode 100644
index 0000000..952d27d
--- /dev/null
+++ b/frontend/src/components/ui/DeleteDialog.tsx
@@ -0,0 +1,37 @@
+interface DeleteDialogProps {
+ isOpen: boolean
+ onClose: () => void
+ onConfirm: () => void
+ serverName: string
+}
+
+const DeleteDialog = ({ isOpen, onClose, onConfirm, serverName }: DeleteDialogProps) => {
+ if (!isOpen) return null
+
+ return (
+
+
+
Delete Server
+
+ Are you sure you want to delete server {serverName}? This action cannot be undone.
+
+
+
+
+
+
+
+ )
+}
+
+export default DeleteDialog
\ No newline at end of file
diff --git a/frontend/src/components/ui/ToolCard.tsx b/frontend/src/components/ui/ToolCard.tsx
new file mode 100644
index 0000000..9166538
--- /dev/null
+++ b/frontend/src/components/ui/ToolCard.tsx
@@ -0,0 +1,38 @@
+import { useState } from 'react'
+import { Tool } from '@/types'
+import { ChevronDown, ChevronRight } from '@/components/icons/LucideIcons'
+
+interface ToolCardProps {
+ tool: Tool
+}
+
+const ToolCard = ({ tool }: ToolCardProps) => {
+ const [isExpanded, setIsExpanded] = useState(false)
+
+ return (
+
+
setIsExpanded(!isExpanded)}
+ >
+
{tool.name}
+
+
+ {isExpanded && (
+
+
{tool.description || 'No description available'}
+
+
Input Schema:
+
+ {JSON.stringify(tool.inputSchema, null, 2)}
+
+
+
+ )}
+
+ )
+}
+
+export default ToolCard
\ No newline at end of file
diff --git a/frontend/src/index.css b/frontend/src/index.css
new file mode 100644
index 0000000..a965c8f
--- /dev/null
+++ b/frontend/src/index.css
@@ -0,0 +1,20 @@
+/* Use standard Tailwind directives */
+@import "tailwindcss";
+
+/* Add some custom styles to verify CSS is working correctly */
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.bg-custom-blue {
+ background-color: #4a90e2;
+}
+
+.text-custom-white {
+ color: #ffffff;
+}
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
new file mode 100644
index 0000000..2fbbbc1
--- /dev/null
+++ b/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
\ No newline at end of file
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
new file mode 100644
index 0000000..014b944
--- /dev/null
+++ b/frontend/src/types/index.ts
@@ -0,0 +1,55 @@
+// 服务器状态类型
+export type ServerStatus = 'connecting' | 'connected' | 'disconnected';
+
+// 工具输入模式类型
+export interface ToolInputSchema {
+ type: string;
+ properties?: Record;
+ required?: string[];
+ [key: string]: any;
+}
+
+// 工具类型
+export interface Tool {
+ name: string;
+ description?: string;
+ inputSchema: ToolInputSchema;
+}
+
+// 服务器配置类型
+export interface ServerConfig {
+ url?: string;
+ command?: string;
+ args?: string[] | string;
+ env?: Record;
+}
+
+// 服务器类型
+export interface Server {
+ name: string;
+ status: ServerStatus;
+ tools?: Tool[];
+ config?: ServerConfig;
+}
+
+// 环境变量类型
+export interface EnvVar {
+ key: string;
+ value: string;
+}
+
+// 表单数据类型
+export interface ServerFormData {
+ name: string;
+ url: string;
+ command: string;
+ arguments: string;
+ args: string[];
+}
+
+// API响应类型
+export interface ApiResponse {
+ success: boolean;
+ data: T;
+ message?: string;
+}
\ No newline at end of file
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..e91ebaa
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,31 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noFallthroughCasesInSwitch": true,
+
+ /* Paths */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
\ No newline at end of file
diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json
new file mode 100644
index 0000000..099658c
--- /dev/null
+++ b/frontend/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
\ No newline at end of file
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
new file mode 100644
index 0000000..853e72c
--- /dev/null
+++ b/frontend/vite.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+import tailwindcss from '@tailwindcss/vite';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:3000',
+ changeOrigin: true,
+ },
+ },
+ },
+});
diff --git a/package.json b/package.json
index b8aa8e7..947cd22 100644
--- a/package.json
+++ b/package.json
@@ -7,10 +7,14 @@
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
- "dev": "tsx watch src/index.ts",
+ "backend:dev": "tsx watch src/index.ts",
"lint": "eslint . --ext .ts",
"format": "prettier --write \"src/**/*.ts\"",
- "test": "jest"
+ "test": "jest",
+ "frontend:dev": "cd frontend && vite",
+ "frontend:build": "cd frontend && vite build",
+ "frontend:preview": "cd frontend && vite preview",
+ "dev": "concurrently \"pnpm backend:dev\" \"pnpm frontend:dev\""
},
"keywords": [
"typescript",
@@ -23,6 +27,7 @@
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-slot": "^1.1.2",
"@shadcn/ui": "^0.0.4",
+ "@tailwindcss/vite": "^4.1.3",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"autoprefixer": "^10.4.21",
@@ -41,17 +46,21 @@
"zod": "^3.24.2"
},
"devDependencies": {
+ "@tailwindcss/postcss": "^4.1.3",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.5",
"@types/node": "^20.8.2",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
+ "@vitejs/plugin-react": "^4.2.1",
+ "concurrently": "^8.2.2",
"eslint": "^8.50.0",
"jest": "^29.7.0",
"prettier": "^3.0.3",
"ts-jest": "^29.1.1",
"ts-node-dev": "^2.0.0",
"tsx": "^4.7.0",
- "typescript": "^5.2.2"
+ "typescript": "^5.2.2",
+ "vite": "^5.2.6"
}
-}
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c9760a4..a7a514e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
'@shadcn/ui':
specifier: ^0.0.4
version: 0.0.4
+ '@tailwindcss/vite':
+ specifier: ^4.1.3
+ version: 4.1.3(vite@5.4.17(@types/node@20.17.28)(lightningcss@1.29.2))
'@types/react':
specifier: ^19.0.12
version: 19.0.12
@@ -69,6 +72,9 @@ importers:
specifier: ^3.24.2
version: 3.24.2
devDependencies:
+ '@tailwindcss/postcss':
+ specifier: ^4.1.3
+ version: 4.1.3
'@types/express':
specifier: ^4.17.21
version: 4.17.21
@@ -84,6 +90,12 @@ importers:
'@typescript-eslint/parser':
specifier: ^6.7.4
version: 6.21.0(eslint@8.57.1)(typescript@5.8.2)
+ '@vitejs/plugin-react':
+ specifier: ^4.2.1
+ version: 4.3.4(vite@5.4.17(@types/node@20.17.28)(lightningcss@1.29.2))
+ concurrently:
+ specifier: ^8.2.2
+ version: 8.2.2
eslint:
specifier: ^8.50.0
version: 8.57.1
@@ -105,9 +117,16 @@ importers:
typescript:
specifier: ^5.2.2
version: 5.8.2
+ vite:
+ specifier: ^5.2.6
+ version: 5.4.17(@types/node@20.17.28)(lightningcss@1.29.2)
packages:
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
@@ -258,6 +277,22 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-transform-react-jsx-self@7.25.9':
+ resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9':
+ resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.27.0':
+ resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/template@7.27.0':
resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
engines: {node: '>=6.9.0'}
@@ -280,102 +315,204 @@ packages:
'@emnapi/runtime@1.4.0':
resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==}
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
'@esbuild/aix-ppc64@0.25.2':
resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
'@esbuild/android-arm64@0.25.2':
resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
'@esbuild/android-arm@0.25.2':
resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
'@esbuild/android-x64@0.25.2':
resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
'@esbuild/darwin-arm64@0.25.2':
resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
'@esbuild/darwin-x64@0.25.2':
resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
'@esbuild/freebsd-arm64@0.25.2':
resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
'@esbuild/freebsd-x64@0.25.2':
resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
'@esbuild/linux-arm64@0.25.2':
resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
'@esbuild/linux-arm@0.25.2':
resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
'@esbuild/linux-ia32@0.25.2':
resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
'@esbuild/linux-loong64@0.25.2':
resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
'@esbuild/linux-mips64el@0.25.2':
resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
'@esbuild/linux-ppc64@0.25.2':
resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
'@esbuild/linux-riscv64@0.25.2':
resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
'@esbuild/linux-s390x@0.25.2':
resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
'@esbuild/linux-x64@0.25.2':
resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==}
engines: {node: '>=18'}
@@ -388,6 +525,12 @@ packages:
cpu: [arm64]
os: [netbsd]
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
'@esbuild/netbsd-x64@0.25.2':
resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==}
engines: {node: '>=18'}
@@ -400,30 +543,60 @@ packages:
cpu: [arm64]
os: [openbsd]
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
'@esbuild/openbsd-x64@0.25.2':
resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
'@esbuild/sunos-x64@0.25.2':
resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
'@esbuild/win32-arm64@0.25.2':
resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
'@esbuild/win32-ia32@0.25.2':
resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
'@esbuild/win32-x64@0.25.2':
resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==}
engines: {node: '>=18'}
@@ -487,67 +660,79 @@ packages:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -685,24 +870,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@next/swc-linux-arm64-musl@15.2.4':
resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@next/swc-linux-x64-gnu@15.2.4':
resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@next/swc-linux-x64-musl@15.2.4':
resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@next/swc-win32-arm64-msvc@15.2.4':
resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
@@ -868,6 +1057,117 @@ packages:
'@types/react':
optional: true
+ '@rollup/rollup-android-arm-eabi@4.39.0':
+ resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.39.0':
+ resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.39.0':
+ resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.39.0':
+ resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.39.0':
+ resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.39.0':
+ resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.39.0':
+ resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.39.0':
+ resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.39.0':
+ resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.39.0':
+ resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.39.0':
+ resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.39.0':
+ resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.39.0':
+ resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.39.0':
+ resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.39.0':
+ resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.39.0':
+ resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.39.0':
+ resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-win32-arm64-msvc@4.39.0':
+ resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.39.0':
+ resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.39.0':
+ resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==}
+ cpu: [x64]
+ os: [win32]
+
'@shadcn/ui@0.0.4':
resolution: {integrity: sha512-0dtu/5ApsOZ24qgaZwtif8jVwqol7a4m1x5AxPuM1k5wxhqU7t/qEfBGtaSki1R8VlbTQfCj5PAlO45NKCa7Gg==}
hasBin: true
@@ -887,6 +1187,91 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+ '@tailwindcss/node@4.1.3':
+ resolution: {integrity: sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.3':
+ resolution: {integrity: sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.3':
+ resolution: {integrity: sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.3':
+ resolution: {integrity: sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.3':
+ resolution: {integrity: sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
+ resolution: {integrity: sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
+ resolution: {integrity: sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.3':
+ resolution: {integrity: sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.3':
+ resolution: {integrity: sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.3':
+ resolution: {integrity: sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
+ resolution: {integrity: sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.3':
+ resolution: {integrity: sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.3':
+ resolution: {integrity: sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/postcss@4.1.3':
+ resolution: {integrity: sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==}
+
+ '@tailwindcss/vite@4.1.3':
+ resolution: {integrity: sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6
+
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@@ -917,6 +1302,9 @@ packages:
'@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
'@types/express-serve-static-core@4.19.6':
resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==}
@@ -1049,6 +1437,12 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+ '@vitejs/plugin-react@4.3.4':
+ resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0
+
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -1306,6 +1700,11 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ concurrently@8.2.2:
+ resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==}
+ engines: {node: ^14.13.0 || >=16.0.0}
+ hasBin: true
+
content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
@@ -1355,6 +1754,10 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
+ date-fns@2.30.0:
+ resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
+ engines: {node: '>=0.11'}
+
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
@@ -1468,6 +1871,10 @@ packages:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
+ enhanced-resolve@5.18.1:
+ resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
+ engines: {node: '>=10.13.0'}
+
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
@@ -1483,6 +1890,11 @@ packages:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
esbuild@0.25.2:
resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==}
engines: {node: '>=18'}
@@ -2020,6 +2432,10 @@ packages:
node-notifier:
optional: true
+ jiti@2.4.2:
+ resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+ hasBin: true
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -2071,6 +2487,74 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
+ lightningcss-darwin-arm64@1.29.2:
+ resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.29.2:
+ resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.29.2:
+ resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.29.2:
+ resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.29.2:
+ resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
+ engines: {node: '>= 12.0.0'}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -2088,6 +2572,9 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
log-symbols@5.1.0:
resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==}
engines: {node: '>=12'}
@@ -2450,6 +2937,10 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+ react-refresh@0.14.2:
+ resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
+ engines: {node: '>=0.10.0'}
+
react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'}
@@ -2462,6 +2953,9 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -2508,6 +3002,11 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
+ rollup@4.39.0:
+ resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
@@ -2515,6 +3014,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -2564,6 +3066,10 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
+ shell-quote@1.8.2:
+ resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==}
+ engines: {node: '>= 0.4'}
+
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
@@ -2607,6 +3113,9 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
+ spawn-command@0.0.2:
+ resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
+
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
@@ -2705,6 +3214,13 @@ packages:
tailwindcss@4.0.17:
resolution: {integrity: sha512-OErSiGzRa6rLiOvaipsDZvLMSpsBZ4ysB4f0VKGXUrjw2jfkJRd6kjRKV2+ZmTCNvwtvgdDam5D7w6WXsdLJZw==}
+ tailwindcss@4.1.3:
+ resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
@@ -2864,6 +3380,37 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
+ vite@5.4.17:
+ resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
@@ -2931,6 +3478,8 @@ packages:
snapshots:
+ '@alloc/quick-lru@5.2.0': {}
+
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.8
@@ -3098,6 +3647,20 @@ snapshots:
'@babel/core': 7.26.10
'@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/runtime@7.27.0':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
'@babel/template@7.27.0':
dependencies:
'@babel/code-frame': 7.26.2
@@ -3132,78 +3695,147 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
'@esbuild/aix-ppc64@0.25.2':
optional: true
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
'@esbuild/android-arm64@0.25.2':
optional: true
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
'@esbuild/android-arm@0.25.2':
optional: true
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
'@esbuild/android-x64@0.25.2':
optional: true
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
'@esbuild/darwin-arm64@0.25.2':
optional: true
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
'@esbuild/darwin-x64@0.25.2':
optional: true
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
'@esbuild/freebsd-arm64@0.25.2':
optional: true
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
'@esbuild/freebsd-x64@0.25.2':
optional: true
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
'@esbuild/linux-arm64@0.25.2':
optional: true
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
'@esbuild/linux-arm@0.25.2':
optional: true
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
'@esbuild/linux-ia32@0.25.2':
optional: true
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
'@esbuild/linux-loong64@0.25.2':
optional: true
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
'@esbuild/linux-mips64el@0.25.2':
optional: true
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
'@esbuild/linux-ppc64@0.25.2':
optional: true
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
'@esbuild/linux-riscv64@0.25.2':
optional: true
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
'@esbuild/linux-s390x@0.25.2':
optional: true
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
'@esbuild/linux-x64@0.25.2':
optional: true
'@esbuild/netbsd-arm64@0.25.2':
optional: true
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
'@esbuild/netbsd-x64@0.25.2':
optional: true
'@esbuild/openbsd-arm64@0.25.2':
optional: true
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
'@esbuild/openbsd-x64@0.25.2':
optional: true
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
'@esbuild/sunos-x64@0.25.2':
optional: true
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
'@esbuild/win32-arm64@0.25.2':
optional: true
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
'@esbuild/win32-ia32@0.25.2':
optional: true
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
'@esbuild/win32-x64@0.25.2':
optional: true
@@ -3681,6 +4313,66 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.12
+ '@rollup/rollup-android-arm-eabi@4.39.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.39.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.39.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.39.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.39.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.39.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.39.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.39.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.39.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.39.0':
+ optional: true
+
'@shadcn/ui@0.0.4':
dependencies:
chalk: 5.2.0
@@ -3708,6 +4400,75 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@tailwindcss/node@4.1.3':
+ dependencies:
+ enhanced-resolve: 5.18.1
+ jiti: 2.4.2
+ lightningcss: 1.29.2
+ tailwindcss: 4.1.3
+
+ '@tailwindcss/oxide-android-arm64@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.3':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.3':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.3
+ '@tailwindcss/oxide-darwin-arm64': 4.1.3
+ '@tailwindcss/oxide-darwin-x64': 4.1.3
+ '@tailwindcss/oxide-freebsd-x64': 4.1.3
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.3
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.3
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.3
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.3
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.3
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.3
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.3
+
+ '@tailwindcss/postcss@4.1.3':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.1.3
+ '@tailwindcss/oxide': 4.1.3
+ postcss: 8.5.3
+ tailwindcss: 4.1.3
+
+ '@tailwindcss/vite@4.1.3(vite@5.4.17(@types/node@20.17.28)(lightningcss@1.29.2))':
+ dependencies:
+ '@tailwindcss/node': 4.1.3
+ '@tailwindcss/oxide': 4.1.3
+ tailwindcss: 4.1.3
+ vite: 5.4.17(@types/node@20.17.28)(lightningcss@1.29.2)
+
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
@@ -3746,6 +4507,8 @@ snapshots:
dependencies:
'@types/node': 20.17.28
+ '@types/estree@1.0.7': {}
+
'@types/express-serve-static-core@4.19.6':
dependencies:
'@types/node': 20.17.28
@@ -3914,6 +4677,17 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
+ '@vitejs/plugin-react@4.3.4(vite@5.4.17(@types/node@20.17.28)(lightningcss@1.29.2))':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10)
+ '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.14.2
+ vite: 5.4.17(@types/node@20.17.28)(lightningcss@1.29.2)
+ transitivePeerDependencies:
+ - supports-color
+
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@@ -4214,6 +4988,18 @@ snapshots:
concat-map@0.0.1: {}
+ concurrently@8.2.2:
+ dependencies:
+ chalk: 4.1.2
+ date-fns: 2.30.0
+ lodash: 4.17.21
+ rxjs: 7.8.2
+ shell-quote: 1.8.2
+ spawn-command: 0.0.2
+ supports-color: 8.1.1
+ tree-kill: 1.2.2
+ yargs: 17.7.2
+
content-disposition@0.5.4:
dependencies:
safe-buffer: 5.2.1
@@ -4264,6 +5050,10 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
+ date-fns@2.30.0:
+ dependencies:
+ '@babel/runtime': 7.27.0
+
debug@2.6.9:
dependencies:
ms: 2.0.0
@@ -4290,8 +5080,7 @@ snapshots:
destroy@1.2.0: {}
- detect-libc@2.0.3:
- optional: true
+ detect-libc@2.0.3: {}
detect-newline@3.1.0: {}
@@ -4335,6 +5124,11 @@ snapshots:
encodeurl@2.0.0: {}
+ enhanced-resolve@5.18.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
error-ex@1.3.2:
dependencies:
is-arrayish: 0.2.1
@@ -4347,6 +5141,32 @@ snapshots:
dependencies:
es-errors: 1.3.0
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
esbuild@0.25.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.2
@@ -5196,6 +6016,8 @@ snapshots:
- supports-color
- ts-node
+ jiti@2.4.2: {}
+
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -5238,6 +6060,51 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
+ lightningcss-darwin-arm64@1.29.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.29.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.29.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ optional: true
+
+ lightningcss@1.29.2:
+ dependencies:
+ detect-libc: 2.0.3
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.29.2
+ lightningcss-darwin-x64: 1.29.2
+ lightningcss-freebsd-x64: 1.29.2
+ lightningcss-linux-arm-gnueabihf: 1.29.2
+ lightningcss-linux-arm64-gnu: 1.29.2
+ lightningcss-linux-arm64-musl: 1.29.2
+ lightningcss-linux-x64-gnu: 1.29.2
+ lightningcss-linux-x64-musl: 1.29.2
+ lightningcss-win32-arm64-msvc: 1.29.2
+ lightningcss-win32-x64-msvc: 1.29.2
+
lines-and-columns@1.2.4: {}
locate-path@5.0.0:
@@ -5252,6 +6119,8 @@ snapshots:
lodash.merge@4.6.2: {}
+ lodash@4.17.21: {}
+
log-symbols@5.1.0:
dependencies:
chalk: 5.2.0
@@ -5564,6 +6433,8 @@ snapshots:
react-is@18.3.1: {}
+ react-refresh@0.14.2: {}
+
react@19.1.0: {}
readable-stream@3.6.2:
@@ -5576,6 +6447,8 @@ snapshots:
dependencies:
picomatch: 2.3.1
+ regenerator-runtime@0.14.1: {}
+
require-directory@2.1.1: {}
resolve-cwd@3.0.0:
@@ -5611,6 +6484,32 @@ snapshots:
dependencies:
glob: 7.2.3
+ rollup@4.39.0:
+ dependencies:
+ '@types/estree': 1.0.7
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.39.0
+ '@rollup/rollup-android-arm64': 4.39.0
+ '@rollup/rollup-darwin-arm64': 4.39.0
+ '@rollup/rollup-darwin-x64': 4.39.0
+ '@rollup/rollup-freebsd-arm64': 4.39.0
+ '@rollup/rollup-freebsd-x64': 4.39.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.39.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.39.0
+ '@rollup/rollup-linux-arm64-gnu': 4.39.0
+ '@rollup/rollup-linux-arm64-musl': 4.39.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.39.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.39.0
+ '@rollup/rollup-linux-riscv64-musl': 4.39.0
+ '@rollup/rollup-linux-s390x-gnu': 4.39.0
+ '@rollup/rollup-linux-x64-gnu': 4.39.0
+ '@rollup/rollup-linux-x64-musl': 4.39.0
+ '@rollup/rollup-win32-arm64-msvc': 4.39.0
+ '@rollup/rollup-win32-ia32-msvc': 4.39.0
+ '@rollup/rollup-win32-x64-msvc': 4.39.0
+ fsevents: 2.3.3
+
router@2.2.0:
dependencies:
debug: 4.4.0
@@ -5625,6 +6524,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
safe-buffer@5.2.1: {}
safer-buffer@2.1.2: {}
@@ -5722,6 +6625,8 @@ snapshots:
shebang-regex@3.0.0: {}
+ shell-quote@1.8.2: {}
+
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -5775,6 +6680,8 @@ snapshots:
source-map@0.6.1: {}
+ spawn-command@0.0.2: {}
+
sprintf-js@1.0.3: {}
stack-utils@2.0.6:
@@ -5849,6 +6756,10 @@ snapshots:
tailwindcss@4.0.17: {}
+ tailwindcss@4.1.3: {}
+
+ tapable@2.2.1: {}
+
test-exclude@6.0.0:
dependencies:
'@istanbuljs/schema': 0.1.3
@@ -5998,6 +6909,16 @@ snapshots:
vary@1.1.2: {}
+ vite@5.4.17(@types/node@20.17.28)(lightningcss@1.29.2):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.5.3
+ rollup: 4.39.0
+ optionalDependencies:
+ '@types/node': 20.17.28
+ fsevents: 2.3.3
+ lightningcss: 1.29.2
+
walker@1.0.8:
dependencies:
makeerror: 1.0.12
diff --git a/public/components/DeleteDialog.jsx b/public/components/DeleteDialog.jsx
deleted file mode 100644
index 59010ce..0000000
--- a/public/components/DeleteDialog.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-// Reusable confirmation dialog component for server deletion
-// Props:
-// - isOpen: boolean - Controls dialog visibility
-// - onClose: () => void - Handler for dialog dismissal
-// - onConfirm: () => void - Handler for delete confirmation
-// - serverName: string - Name of the server to be deleted
-window.DeleteDialog = function DeleteDialog({ isOpen, onClose, onConfirm, serverName }) {
- return (
-
-
-
-
Delete Server
-
-
- Are you sure you want to delete server {serverName}? This action cannot be undone.
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/public/components/LucideIcons.jsx b/public/components/LucideIcons.jsx
deleted file mode 100644
index f9dc08f..0000000
--- a/public/components/LucideIcons.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-// Lightweight implementation of Lucide icons without external dependencies
-// Each icon component accepts:
-// - size: number (default: 24) - Icon dimensions in pixels
-// - className: string - Additional CSS classes
-
-const ChevronDown = ({ size = 24, className = "" }) => (
-
-);
-
-const ChevronRight = ({ size = 24, className = "" }) => (
-
-);
-
-// Export icons to global scope for use in other components
-window.LucideIcons = {
- ChevronDown,
- ChevronRight
-};
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index 1209fbe..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
- MCP Hub Dashboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/public/js/app.js b/public/js/app.js
deleted file mode 100644
index 400e9ee..0000000
--- a/public/js/app.js
+++ /dev/null
@@ -1,677 +0,0 @@
-const { useState, useEffect, Fragment } = React;
-const { ChevronDown, ChevronRight } = window.LucideIcons || {};
-
-// Status badge component with predefined color schemes
-function Badge({ status }) {
- const colors = {
- connecting: 'bg-yellow-100 text-yellow-800',
- connected: 'bg-green-100 text-green-800',
- disconnected: 'bg-red-100 text-red-800',
- };
-
- return (
-
- {status}
-
- );
-}
-
-// Displays tool details with expandable input schema
-function ToolCard({ tool }) {
- const [isExpanded, setIsExpanded] = useState(false);
-
- return (
-
-
setIsExpanded(!isExpanded)}
- >
-
{tool.name}
-
-
- {isExpanded && (
-
-
{tool.description || 'No description available'}
-
-
Input Schema:
-
- {JSON.stringify(tool.inputSchema, null, 2)}
-
-
-
- )}
-
- );
-}
-
-// Delete confirmation dialog component
-function DeleteDialog({ isOpen, onClose, onConfirm, serverName }) {
- if (!isOpen) return null;
-
- return (
-
-
-
Confirm Deletion
-
- Are you sure you want to delete the server {serverName}? This action
- cannot be undone.
-
-
-
-
-
-
-
- );
-}
-
-// Main server card component for displaying server status and available tools
-function ServerCard({ server, onRemove, onEdit }) {
- const [isExpanded, setIsExpanded] = useState(false);
- const [showDeleteDialog, setShowDeleteDialog] = useState(false);
-
- const handleRemove = (e) => {
- e.stopPropagation();
- setShowDeleteDialog(true);
- };
-
- const handleEdit = (e) => {
- e.stopPropagation();
- onEdit(server);
- };
-
- const handleConfirmDelete = () => {
- onRemove(server.name);
- setShowDeleteDialog(false);
- };
-
- return (
-
-
setIsExpanded(!isExpanded)}
- >
-
-
{server.name}
-
-
-
-
-
-
-
-
-
-
setShowDeleteDialog(false)}
- onConfirm={handleConfirmDelete}
- serverName={server.name}
- />
-
- {isExpanded && server.tools && (
-
-
Available Tools
-
- {server.tools.map((tool, index) => (
-
- ))}
-
-
- )}
-
- );
-}
-
-// Form component for adding/editing MCP servers with stdio or SSE protocol support
-function ServerForm({ onSubmit, onCancel, initialData = null, modalTitle }) {
- const [serverType, setServerType] = useState(
- initialData && initialData.config && initialData.config.url ? 'sse' : 'stdio',
- );
-
- const [formData, setFormData] = useState({
- name: (initialData && initialData.name) || '',
- url: (initialData && initialData.config && initialData.config.url) || '',
- command: (initialData && initialData.config && initialData.config.command) || '',
- arguments:
- initialData && initialData.config && initialData.config.args
- ? Array.isArray(initialData.config.args)
- ? initialData.config.args.join(' ')
- : String(initialData.config.args)
- : '',
- args: (initialData && initialData.config && initialData.config.args) || [],
- });
-
- const [envVars, setEnvVars] = useState(
- initialData && initialData.config && initialData.config.env
- ? Object.entries(initialData.config.env).map(([key, value]) => ({ key, value }))
- : [],
- );
-
- const [error, setError] = useState(null);
- const isEdit = !!initialData;
-
- const handleInputChange = (e) => {
- const { name, value } = e.target;
- setFormData({ ...formData, [name]: value });
- };
-
- // Transform space-separated arguments string into array
- const handleArgsChange = (value) => {
- let args = value.split(' ').filter((arg) => arg.trim() !== '');
- setFormData({ ...formData, arguments: value, args });
- };
-
- const handleEnvVarChange = (index, field, value) => {
- const newEnvVars = [...envVars];
- newEnvVars[index][field] = value;
- setEnvVars(newEnvVars);
- };
-
- const addEnvVar = () => {
- setEnvVars([...envVars, { key: '', value: '' }]);
- };
-
- const removeEnvVar = (index) => {
- const newEnvVars = [...envVars];
- newEnvVars.splice(index, 1);
- setEnvVars(newEnvVars);
- };
-
- // Submit handler for server configuration
- const handleSubmit = async (e) => {
- e.preventDefault();
- setError(null);
-
- try {
- const env = {};
- envVars.forEach(({ key, value }) => {
- if (key.trim()) {
- env[key.trim()] = value;
- }
- });
-
- const payload = {
- name: formData.name,
- config:
- serverType === 'sse'
- ? { url: formData.url }
- : {
- command: formData.command,
- args: formData.args,
- env: Object.keys(env).length > 0 ? env : undefined,
- },
- };
-
- onSubmit(payload);
- } catch (err) {
- setError('Error: ' + err.message);
- }
- };
-
- return (
-
-
-
{modalTitle}
-
-
-
- {error &&
{error}
}
-
-
-
- );
-}
-
-// Form component for adding new MCP servers (wrapper around ServerForm)
-function AddServerForm({ onAdd }) {
- const [modalVisible, setModalVisible] = useState(false);
-
- const toggleModal = () => {
- setModalVisible(!modalVisible);
- };
-
- const handleSubmit = async (payload) => {
- try {
- const response = await fetch('/api/servers', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- });
-
- const result = await response.json();
-
- if (!response.ok) {
- alert(result.message || 'Failed to add server');
- return;
- }
-
- setModalVisible(false);
- onAdd();
- } catch (err) {
- alert('Error: ' + err.message);
- }
- };
-
- return (
-
-
-
- {modalVisible && (
-
-
-
- )}
-
- );
-}
-
-// Form component for editing MCP servers (wrapper around ServerForm)
-function EditServerForm({ server, onEdit, onCancel }) {
- const handleSubmit = async (payload) => {
- try {
- const response = await fetch(`/api/servers/${server.name}`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- });
-
- const result = await response.json();
-
- if (!response.ok) {
- alert(result.message || 'Failed to update server');
- return;
- }
-
- onEdit();
- } catch (err) {
- alert('Error: ' + err.message);
- }
- };
-
- return (
-
-
-
- );
-}
-
-// Root application component managing server state and UI
-function App() {
- const [servers, setServers] = useState([]);
- const [error, setError] = useState(null);
- const [refreshKey, setRefreshKey] = useState(0);
- const [editingServer, setEditingServer] = useState(null);
-
- useEffect(() => {
- fetch('/api/servers')
- .then((response) => response.json())
- .then((data) => {
- // 处理API响应中的包装对象,提取data字段
- if (data && data.success && Array.isArray(data.data)) {
- setServers(data.data);
- } else if (data && Array.isArray(data)) {
- // 兼容性处理,如果API直接返回数组
- setServers(data);
- } else {
- // 如果数据格式不符合预期,设置为空数组
- console.error('Invalid server data format:', data);
- setServers([]);
- }
- })
- .catch((err) => setError(err.message));
-
- // Poll for updates every 5 seconds
- const interval = setInterval(() => {
- fetch('/api/servers')
- .then((response) => response.json())
- .then((data) => {
- // 处理API响应中的包装对象,提取data字段
- if (data && data.success && Array.isArray(data.data)) {
- setServers(data.data);
- } else if (data && Array.isArray(data)) {
- // 兼容性处理,如果API直接返回数组
- setServers(data);
- } else {
- // 如果数据格式不符合预期,设置为空数组
- console.error('Invalid server data format:', data);
- setServers([]);
- }
- })
- .catch((err) => setError(err.message));
- }, 5000);
-
- return () => clearInterval(interval);
- }, [refreshKey]);
-
- const handleServerAdd = () => {
- setRefreshKey((prevKey) => prevKey + 1);
- };
-
- const handleServerEdit = (server) => {
- // Fetch settings to get the full server config before editing
- fetch(`/api/settings`)
- .then((response) => response.json())
- .then((settingsData) => {
- if (
- settingsData &&
- settingsData.success &&
- settingsData.data &&
- settingsData.data.mcpServers &&
- settingsData.data.mcpServers[server.name]
- ) {
- const serverConfig = settingsData.data.mcpServers[server.name];
- const fullServerData = {
- name: server.name,
- status: server.status,
- tools: server.tools || [],
- config: serverConfig,
- };
-
- console.log('Editing server with config:', fullServerData);
- setEditingServer(fullServerData);
- } else {
- console.error('Failed to get server config from settings:', settingsData);
- setError(`Could not find configuration data for ${server.name}`);
- }
- })
- .catch((err) => {
- console.error('Error fetching server settings:', err);
- setError(err.message);
- });
- };
-
- const handleEditComplete = () => {
- setEditingServer(null);
- setRefreshKey((prevKey) => prevKey + 1);
- };
-
- const handleServerRemove = async (serverName) => {
- try {
- const response = await fetch(`/api/servers/${serverName}`, {
- method: 'DELETE',
- });
- const result = await response.json();
-
- if (!response.ok) {
- setError(result.message || `Failed to delete server ${serverName}`);
- return;
- }
-
- setRefreshKey((prevKey) => prevKey + 1);
- } catch (err) {
- setError('Error: ' + err.message);
- }
- };
-
- if (error) {
- return (
-
-
-
-
Error
-
{error}
-
-
-
-
- );
- }
-
- return (
-
-
-
- {servers.length === 0 ? (
-
-
No MCP servers available
-
- ) : (
-
- {servers.map((server, index) => (
-
- ))}
-
- )}
- {editingServer && (
-
setEditingServer(null)}
- />
- )}
-
-
- );
-}
-
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render();
diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts
index c7e8028..39e9378 100644
--- a/src/middlewares/index.ts
+++ b/src/middlewares/index.ts
@@ -15,7 +15,7 @@ export const errorHandler = (
};
export const initMiddlewares = (app: express.Application): void => {
- app.use(express.static('public'));
+ app.use(express.static('frontend/dist'));
app.use((req, res, next) => {
if (req.path !== '/sse' && req.path !== '/messages') {
@@ -26,7 +26,7 @@ export const initMiddlewares = (app: express.Application): void => {
});
app.get('/', (_req: Request, res: Response) => {
- res.sendFile(path.join(process.cwd(), 'public', 'index.html'));
+ res.sendFile(path.join(process.cwd(), 'frontend', 'dist', 'index.html'));
});
app.use(errorHandler);