- {showTimestamp && (
+ {showDateDivider && (
- {new Date(message.createdAt).toLocaleDateString()}
+ {formatDateDivider(message.createdAt)}
)}
@@ -101,8 +99,14 @@ export default function MessageList({ messages, typingUsers }) {
)}
- {formatTime(message.createdAt)}
- {message.editedAt && edited}
+
+ {formatMessageTime(message.createdAt)}
+
+ {message.editedAt && (
+
+ {formatEditedBadge(message.editedAt)}
+
+ )}
{message._sending && sending...}
@@ -123,14 +127,7 @@ export default function MessageList({ messages, typingUsers }) {
})}
{typingUsers.length > 0 && (
-
-
-
-
-
-
-
Someone is typing...
-
+
)}
diff --git a/astro-site/src/react-app/components/Chat/MessageReactions.css b/astro-site/src/react-app/components/Chat/MessageReactions.css
new file mode 100644
index 0000000..7716fb5
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/MessageReactions.css
@@ -0,0 +1,97 @@
+/**
+ * MessageReactions CSS
+ * Displays emoji reactions on messages
+ */
+
+.message-reactions-container {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: 0.5rem;
+ flex-wrap: wrap;
+ position: relative;
+}
+
+.reactions-list {
+ display: flex;
+ gap: 0.4rem;
+ flex-wrap: wrap;
+}
+
+.reaction-button {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.35rem 0.6rem;
+ border: 1px solid #7289da;
+ background: rgba(114, 137, 218, 0.15);
+ border-radius: 12px;
+ cursor: pointer;
+ transition: all 0.2s;
+ font-size: 0.875rem;
+}
+
+.reaction-button:hover {
+ background: rgba(114, 137, 218, 0.25);
+ border-color: #00d9ff;
+}
+
+.reaction-button.user-reacted {
+ background: rgba(0, 217, 255, 0.2);
+ border-color: #00d9ff;
+}
+
+.reaction-button.user-reacted:hover {
+ background: rgba(0, 217, 255, 0.3);
+}
+
+.reaction-emoji {
+ font-size: 1.125rem;
+}
+
+.reaction-count {
+ font-size: 0.75rem;
+ color: #b5bac1;
+ font-weight: 500;
+}
+
+.add-reaction-container {
+ position: relative;
+}
+
+.btn-add-reaction {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid #7289da;
+ background: rgba(114, 137, 218, 0.15);
+ border-radius: 50%;
+ cursor: pointer;
+ transition: all 0.2s;
+ font-size: 0.875rem;
+ padding: 0;
+}
+
+.btn-add-reaction:hover {
+ background: rgba(114, 137, 218, 0.25);
+ transform: scale(1.1);
+}
+
+@media (max-width: 480px) {
+ .message-reactions-container {
+ gap: 0.35rem;
+ }
+
+ .reaction-button {
+ padding: 0.3rem 0.5rem;
+ font-size: 0.8125rem;
+ }
+
+ .btn-add-reaction {
+ width: 24px;
+ height: 24px;
+ font-size: 0.75rem;
+ }
+}
diff --git a/astro-site/src/react-app/components/Chat/MessageReactions.jsx b/astro-site/src/react-app/components/Chat/MessageReactions.jsx
new file mode 100644
index 0000000..18cc0e9
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/MessageReactions.jsx
@@ -0,0 +1,74 @@
+/**
+ * MessageReactions Component
+ * Displays and manages emoji reactions on messages
+ */
+
+import React, { useState } from 'react';
+import ReactionPicker from './ReactionPicker';
+import './MessageReactions.css';
+
+export default function MessageReactions({
+ reactions = [],
+ onAddReaction,
+ onRemoveReaction,
+ currentUserId,
+ messageId,
+}) {
+ const [showPicker, setShowPicker] = useState(false);
+
+ const handleReactionClick = (emoji) => {
+ // Check if user already reacted with this emoji
+ const existingReaction = reactions.find((r) => r.emoji === emoji);
+
+ if (existingReaction?.users?.includes(currentUserId)) {
+ // Remove reaction
+ onRemoveReaction?.(messageId, emoji, currentUserId);
+ } else {
+ // Add reaction
+ onAddReaction?.(messageId, emoji, currentUserId);
+ }
+ };
+
+ const handleAddReaction = (emoji) => {
+ onAddReaction?.(messageId, emoji, currentUserId);
+ };
+
+ return (
+
+
+ {reactions.map((reaction, idx) => {
+ const userReacted = reaction.users?.includes(currentUserId);
+ return (
+
+ );
+ })}
+
+
+
+ {showPicker && (
+ setShowPicker(false)}
+ />
+ )}
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Chat/ReactionPicker.css b/astro-site/src/react-app/components/Chat/ReactionPicker.css
new file mode 100644
index 0000000..5de5a35
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/ReactionPicker.css
@@ -0,0 +1,45 @@
+/**
+ * ReactionPicker CSS
+ */
+
+.reaction-picker-wrapper {
+ position: absolute;
+ bottom: 40px;
+ right: -10px;
+ z-index: 1000;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.reaction-picker-wrapper :global(.em-emoji-picker) {
+ background: #2c2f33;
+ border: 1px solid #23272a;
+}
+
+.reaction-picker-wrapper :global(.em-emoji-picker-header) {
+ background: #23272a;
+ border-bottom: 1px solid #1a1d1f;
+}
+
+.reaction-picker-wrapper :global(.em-emoji-picker-search input) {
+ background: #23272a !important;
+ color: #ffffff !important;
+ border: 1px solid #1a1d1f !important;
+}
+
+.reaction-picker-wrapper :global(.em-category-button) {
+ color: #72767d;
+}
+
+.reaction-picker-wrapper :global(.em-category-button.active) {
+ color: #7289da;
+}
+
+.reaction-picker-wrapper :global(.em-emoji) {
+ cursor: pointer;
+}
+
+.reaction-picker-wrapper :global(.em-emoji:hover) {
+ transform: scale(1.1);
+}
diff --git a/astro-site/src/react-app/components/Chat/SearchMessages.css b/astro-site/src/react-app/components/Chat/SearchMessages.css
new file mode 100644
index 0000000..94706f1
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/SearchMessages.css
@@ -0,0 +1,252 @@
+/**
+ * SearchMessages CSS
+ */
+
+.search-messages-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ display: flex;
+ align-items: flex-start;
+ justify-content: flex-end;
+ z-index: 2000;
+ animation: fadeIn 0.2s ease-in-out;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.search-messages-panel {
+ background: #2c2f33;
+ width: 100%;
+ max-width: 400px;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: -4px 0 16px rgba(0, 0, 0, 0.5);
+ animation: slideInRight 0.3s ease-out;
+}
+
+@keyframes slideInRight {
+ from {
+ transform: translateX(100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+.search-header {
+ padding: 1.5rem;
+ border-bottom: 1px solid #23272a;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: #23272a;
+}
+
+.search-header h3 {
+ margin: 0;
+ font-size: 1.125rem;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ color: #72767d;
+ font-size: 1.5rem;
+ cursor: pointer;
+ transition: color 0.2s;
+ padding: 0;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.close-btn:hover {
+ color: #ffffff;
+}
+
+.search-input-group {
+ padding: 1rem 1.5rem;
+ position: relative;
+}
+
+.search-input {
+ width: 100%;
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
+ border: 1px solid #7289da;
+ background: rgba(114, 137, 218, 0.1);
+ color: #ffffff;
+ border-radius: 20px;
+ font-size: 0.9375rem;
+ outline: none;
+ transition: border-color 0.2s;
+}
+
+.search-input:focus {
+ border-color: #00d9ff;
+ background: rgba(0, 217, 255, 0.05);
+}
+
+.search-input::placeholder {
+ color: #72767d;
+}
+
+.search-icon {
+ position: absolute;
+ left: 2rem;
+ top: 50%;
+ transform: translateY(-50%);
+ color: #72767d;
+}
+
+.filter-buttons {
+ display: flex;
+ gap: 0.5rem;
+ padding: 0 1.5rem 1rem 1.5rem;
+ overflow-x: auto;
+ scroll-behavior: smooth;
+}
+
+.filter-btn {
+ padding: 0.5rem 1rem;
+ border: 1px solid #7289da;
+ background: transparent;
+ color: #b5bac1;
+ border-radius: 16px;
+ cursor: pointer;
+ font-size: 0.8125rem;
+ font-weight: 500;
+ transition: all 0.2s;
+ white-space: nowrap;
+}
+
+.filter-btn:hover {
+ background: rgba(114, 137, 218, 0.1);
+ color: #ffffff;
+}
+
+.filter-btn.active {
+ background: #7289da;
+ color: #ffffff;
+ border-color: #7289da;
+}
+
+.search-results {
+ flex: 1;
+ padding: 1rem 1.5rem;
+ overflow-y: auto;
+}
+
+.no-results {
+ text-align: center;
+ padding: 3rem 1rem;
+ color: #72767d;
+ font-size: 1rem;
+}
+
+.results-count {
+ color: #b5bac1;
+ font-size: 0.8125rem;
+ margin: 0 0 1rem 0;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.results-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.result-item {
+ background: rgba(114, 137, 218, 0.1);
+ border-left: 3px solid #7289da;
+ padding: 0.75rem;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.result-item:hover {
+ background: rgba(114, 137, 218, 0.2);
+ border-left-color: #00d9ff;
+}
+
+.result-sender {
+ font-size: 0.8125rem;
+ color: #7289da;
+ font-weight: 600;
+ margin-bottom: 0.25rem;
+}
+
+.result-content {
+ font-size: 0.875rem;
+ color: #b5bac1;
+ margin-bottom: 0.5rem;
+ word-break: break-word;
+}
+
+.result-attachments {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.attachment-badge {
+ background: rgba(0, 217, 255, 0.1);
+ border: 1px solid #00d9ff;
+ color: #00d9ff;
+ padding: 0.25rem 0.5rem;
+ border-radius: 3px;
+ font-size: 0.75rem;
+ display: inline-block;
+}
+
+/* Scrollbar styling */
+.search-results::-webkit-scrollbar {
+ width: 6px;
+}
+
+.search-results::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.1);
+}
+
+.search-results::-webkit-scrollbar-thumb {
+ background: rgba(0, 217, 255, 0.2);
+ border-radius: 3px;
+}
+
+.search-results::-webkit-scrollbar-thumb:hover {
+ background: rgba(0, 217, 255, 0.4);
+}
+
+@media (max-width: 768px) {
+ .search-messages-panel {
+ max-width: 100%;
+ }
+
+ .search-input-group,
+ .filter-buttons {
+ padding-left: 1rem;
+ padding-right: 1rem;
+ }
+
+ .search-results {
+ padding: 1rem;
+ }
+}
diff --git a/astro-site/src/react-app/components/Chat/SearchMessages.jsx b/astro-site/src/react-app/components/Chat/SearchMessages.jsx
new file mode 100644
index 0000000..b149650
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/SearchMessages.jsx
@@ -0,0 +1,144 @@
+/**
+ * SearchMessages Component
+ * Search and filter messages by content, sender, or date
+ */
+
+import React, { useState, useEffect, useMemo } from 'react';
+import './SearchMessages.css';
+
+export default function SearchMessages({
+ messages = [],
+ onFilterMessages,
+ onClose,
+ isOpen,
+}) {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterType, setFilterType] = useState('all'); // all, text, attachments, mentions
+ const [results, setResults] = useState([]);
+
+ // Filter messages based on search query and filter type
+ const filteredMessages = useMemo(() => {
+ if (!searchQuery.trim()) return [];
+
+ const query = searchQuery.toLowerCase();
+
+ return messages.filter((msg) => {
+ // Check filter type
+ if (filterType === 'attachments' && (!msg.metadata?.attachments || msg.metadata.attachments.length === 0)) {
+ return false;
+ }
+ if (filterType === 'mentions' && !msg.content.includes('@')) {
+ return false;
+ }
+
+ // Search in content
+ if (msg.content.toLowerCase().includes(query)) {
+ return true;
+ }
+
+ // Search in sender
+ if (msg.senderUsername?.toLowerCase().includes(query)) {
+ return true;
+ }
+
+ // Search in attachments
+ if (msg.metadata?.attachments?.some((att) => att.filename.toLowerCase().includes(query))) {
+ return true;
+ }
+
+ return false;
+ });
+ }, [messages, searchQuery, filterType]);
+
+ useEffect(() => {
+ setResults(filteredMessages);
+ onFilterMessages?.(filteredMessages);
+ }, [filteredMessages, onFilterMessages]);
+
+ if (!isOpen) return null;
+
+ return (
+
+
e.stopPropagation()}>
+
+
Search Messages
+
+
+
+
+ setSearchQuery(e.target.value)}
+ autoFocus
+ />
+ 🔍
+
+
+
+
+
+
+
+
+
+
+ {results.length === 0 ? (
+
+ {searchQuery ? '🔍 No messages found' : '📝 Type to search'}
+
+ ) : (
+ <>
+
{results.length} result(s)
+
+ {results.map((msg, idx) => (
+
+
{msg.senderUsername}
+
+ {msg.content.length > 100
+ ? msg.content.substring(0, 100) + '...'
+ : msg.content}
+
+ {msg.metadata?.attachments?.length > 0 && (
+
+ {msg.metadata.attachments.map((att, i) => (
+
+ 📎 {att.filename}
+
+ ))}
+
+ )}
+
+ ))}
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Chat/TypingIndicator.css b/astro-site/src/react-app/components/Chat/TypingIndicator.css
new file mode 100644
index 0000000..4d0cbb3
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/TypingIndicator.css
@@ -0,0 +1,86 @@
+/**
+ * TypingIndicator CSS
+ * Animated typing indicator with pulsing dots
+ */
+
+.typing-indicator-container {
+ display: flex;
+ align-items: flex-end;
+ gap: 0.75rem;
+ padding: 0.5rem 1rem;
+ animation: slideIn 0.3s ease-out;
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.typing-indicator-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #7289da 0%, #00d9ff 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 0.75rem;
+ font-weight: bold;
+ flex-shrink: 0;
+}
+
+.typing-indicator-content {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.typing-dots {
+ display: flex;
+ gap: 0.25rem;
+}
+
+.typing-dots span {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #7289da;
+ animation: typing-pulse 1.4s infinite;
+}
+
+.typing-dots span:nth-child(1) {
+ animation-delay: 0ms;
+}
+
+.typing-dots span:nth-child(2) {
+ animation-delay: 150ms;
+}
+
+.typing-dots span:nth-child(3) {
+ animation-delay: 300ms;
+}
+
+@keyframes typing-pulse {
+ 0%, 60%, 100% {
+ opacity: 0.3;
+ transform: translateY(0);
+ }
+ 30% {
+ opacity: 1;
+ transform: translateY(-8px);
+ }
+}
+
+.typing-text {
+ color: #b5bac1;
+ font-size: 0.875rem;
+ font-weight: 500;
+ white-space: nowrap;
+}
diff --git a/astro-site/src/react-app/components/Chat/TypingIndicator.jsx b/astro-site/src/react-app/components/Chat/TypingIndicator.jsx
new file mode 100644
index 0000000..c564027
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/TypingIndicator.jsx
@@ -0,0 +1,45 @@
+/**
+ * TypingIndicator Component
+ * Shows animated typing indicator with user names
+ */
+
+import React from 'react';
+import './TypingIndicator.css';
+
+export default function TypingIndicator({ typingUsers, maxDisplay = 3 }) {
+ if (!typingUsers || typingUsers.length === 0) {
+ return null;
+ }
+
+ const displayUsers = typingUsers.slice(0, maxDisplay);
+ const hiddenCount = typingUsers.length - maxDisplay;
+
+ const getTypingText = () => {
+ if (displayUsers.length === 1) {
+ return `${displayUsers[0]} is typing`;
+ }
+ if (displayUsers.length === 2) {
+ return `${displayUsers[0]} and ${displayUsers[1]} are typing`;
+ }
+ if (displayUsers.length === 3) {
+ return `${displayUsers[0]}, ${displayUsers[1]}, and ${displayUsers[2]} are typing`;
+ }
+ return `${displayUsers.join(', ')}, and ${hiddenCount} more are typing`;
+ };
+
+ return (
+
+
+ {displayUsers[0]?.[0]?.toUpperCase() || '?'}
+
+
+
+
+
+
+
+
{getTypingText()}
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Chat/UserStatus.css b/astro-site/src/react-app/components/Chat/UserStatus.css
new file mode 100644
index 0000000..7e7f8a9
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/UserStatus.css
@@ -0,0 +1,66 @@
+/**
+ * UserStatus CSS
+ */
+
+.user-status {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.status-icon {
+ font-size: 1rem;
+ filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5));
+}
+
+/* Sizes */
+.user-status.size-sm .status-icon {
+ font-size: 0.75rem;
+}
+
+.user-status.size-md .status-icon {
+ font-size: 1rem;
+}
+
+.user-status.size-lg .status-icon {
+ font-size: 1.25rem;
+}
+
+/* Animations for online status */
+.user-status.status-online {
+ animation: pulse-online 2s ease-in-out infinite;
+}
+
+@keyframes pulse-online {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.7;
+ }
+}
+
+/* Badge positioning for avatars */
+.user-status-badge {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ background: #2c2f33;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid #2c2f33;
+}
+
+.user-status-badge .user-status {
+ width: 100%;
+ height: 100%;
+}
+
+.user-status-badge .status-icon {
+ font-size: 0.875rem;
+}
diff --git a/astro-site/src/react-app/components/Chat/UserStatus.jsx b/astro-site/src/react-app/components/Chat/UserStatus.jsx
new file mode 100644
index 0000000..37e345d
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/UserStatus.jsx
@@ -0,0 +1,26 @@
+/**
+ * UserStatus Component
+ * Displays user online/offline/idle/dnd status
+ */
+
+import React from 'react';
+import './UserStatus.css';
+
+export default function UserStatus({ status = 'offline', size = 'md' }) {
+ const statusConfig = {
+ online: { label: 'Online', color: '#43b581', icon: '🟢' },
+ idle: { label: 'Idle', color: '#faa61a', icon: '🟡' },
+ dnd: { label: 'Do Not Disturb', color: '#f04747', icon: '🔴' },
+ offline: { label: 'Offline', color: '#747f8d', icon: '⚫' },
+ };
+
+ const config = statusConfig[status] || statusConfig.offline;
+
+ return (
+
+
+ {config.icon}
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Chat/VoiceCallUI.css b/astro-site/src/react-app/components/Chat/VoiceCallUI.css
new file mode 100644
index 0000000..c00c660
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/VoiceCallUI.css
@@ -0,0 +1,158 @@
+/**
+ * VoiceCallUI CSS
+ */
+
+.voice-call-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #1a1d1f;
+ z-index: 5000;
+ display: flex;
+ flex-direction: column;
+}
+
+.voice-call-loading {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: #2c2f33;
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 4px solid rgba(114, 137, 218, 0.2);
+ border-top: 4px solid #7289da;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 1rem;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.voice-call-container > :global(.livekit-room) {
+ flex: 1;
+ width: 100%;
+}
+
+.voice-call-container > :global(.livekit-video-conference) {
+ width: 100%;
+ height: 100%;
+}
+
+/* Controls Footer */
+.voice-call-footer {
+ position: absolute;
+ bottom: 2rem;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 1rem;
+ z-index: 100;
+}
+
+.control-btn {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ border: none;
+ background: rgba(0, 217, 255, 0.1);
+ border: 2px solid #00d9ff;
+ color: #00d9ff;
+ font-size: 1.5rem;
+ cursor: pointer;
+ transition: all 0.3s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.control-btn:hover {
+ background: rgba(0, 217, 255, 0.2);
+ transform: scale(1.1);
+}
+
+.control-btn.muted,
+.control-btn.off {
+ background: rgba(244, 54, 54, 0.1);
+ border-color: #f43636;
+ color: #f43636;
+}
+
+.control-btn.muted:hover,
+.control-btn.off:hover {
+ background: rgba(244, 54, 54, 0.2);
+}
+
+.control-btn.end-call {
+ background: rgba(244, 54, 54, 0.2);
+ border-color: #f43636;
+ color: #f43636;
+}
+
+.control-btn.end-call:hover {
+ background: rgba(244, 54, 54, 0.4);
+}
+
+/* Participant tiles */
+.voice-call-container > :global(.livekit-grid) {
+ width: 100%;
+ height: 100%;
+ padding: 1rem;
+ gap: 1rem;
+}
+
+.voice-call-container > :global(.livekit-participant-tile) {
+ border-radius: 12px;
+ overflow: hidden;
+ border: 2px solid rgba(0, 217, 255, 0.1);
+}
+
+/* Screen share support */
+.voice-call-container > :global(.screen-share-container) {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.voice-call-container > :global(.screen-share-video) {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+/* Mobile responsiveness */
+@media (max-width: 768px) {
+ .control-btn {
+ width: 44px;
+ height: 44px;
+ font-size: 1.25rem;
+ }
+
+ .voice-call-footer {
+ bottom: 1rem;
+ gap: 0.75rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .control-btn {
+ width: 40px;
+ height: 40px;
+ font-size: 1rem;
+ }
+
+ .voice-call-footer {
+ bottom: 0.5rem;
+ gap: 0.5rem;
+ }
+}
diff --git a/astro-site/src/react-app/components/Chat/VoiceCallUI.jsx b/astro-site/src/react-app/components/Chat/VoiceCallUI.jsx
new file mode 100644
index 0000000..1dc11cb
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/VoiceCallUI.jsx
@@ -0,0 +1,74 @@
+/**
+ * VoiceCallUI Component
+ * Real-time voice/video call interface with LiveKit
+ */
+
+import React, { useState, useEffect } from 'react';
+import { LiveKitRoom, VideoConference } from 'livekit-react';
+import './VoiceCallUI.css';
+
+export default function VoiceCallUI({ roomName, userName, token, onClose, onError }) {
+ const [isLoading, setIsLoading] = useState(true);
+ const [isMuted, setIsMuted] = useState(false);
+ const [isVideoOff, setIsVideoOff] = useState(false);
+
+ useEffect(() => {
+ if (token) {
+ setIsLoading(false);
+ }
+ }, [token]);
+
+ if (!token || !roomName) {
+ return (
+
+
+
Initializing call...
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Chat/VoiceVideoCall.css b/astro-site/src/react-app/components/Chat/VoiceVideoCall.css
new file mode 100644
index 0000000..0f7d783
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/VoiceVideoCall.css
@@ -0,0 +1,199 @@
+/**
+ * VoiceVideoCall CSS
+ * Styling for video call interface
+ */
+
+.voice-video-call-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background: #1a1d1f;
+ border-radius: 8px;
+ overflow: hidden;
+ position: relative;
+}
+
+.voice-video-error {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ background: #2c2f33;
+ color: #f43636;
+ font-size: 1.125rem;
+ text-align: center;
+ padding: 2rem;
+}
+
+.call-connecting {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ gap: 1.5rem;
+ color: #b5bac1;
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid rgba(114, 137, 218, 0.2);
+ border-top: 3px solid #7289da;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* LiveKit Room Styles */
+.lk-room {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.lk-video-conference {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: #1a1d1f;
+}
+
+.lk-grid-layout {
+ flex: 1;
+ gap: 0.5rem;
+ padding: 0.5rem;
+ background: #23272a;
+}
+
+.lk-participant-tile {
+ border-radius: 8px;
+ overflow: hidden;
+ background: #2c2f33;
+ border: 1px solid rgba(0, 217, 255, 0.1);
+}
+
+.lk-video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+/* Call Controls */
+.call-controls {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 1rem;
+ padding: 1.5rem;
+ background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.3));
+ border-top: 1px solid rgba(0, 217, 255, 0.1);
+}
+
+.control-btn {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ border: none;
+ background: rgba(0, 217, 255, 0.1);
+ color: #00d9ff;
+ font-size: 1.5rem;
+ cursor: pointer;
+ transition: all 0.3s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.control-btn:hover {
+ background: rgba(0, 217, 255, 0.2);
+ transform: scale(1.05);
+}
+
+.control-btn.active {
+ background: #7289da;
+ color: white;
+}
+
+.control-btn.disabled {
+ background: #f43636;
+ color: white;
+ opacity: 0.8;
+}
+
+.control-btn.leave-btn {
+ background: #f43636;
+ color: white;
+}
+
+.control-btn.leave-btn:hover {
+ background: #d63030;
+ transform: scale(1.08);
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .call-controls {
+ gap: 0.75rem;
+ padding: 1rem;
+ }
+
+ .control-btn {
+ width: 40px;
+ height: 40px;
+ font-size: 1.25rem;
+ }
+
+ .lk-grid-layout {
+ padding: 0.25rem;
+ gap: 0.25rem;
+ }
+}
+
+/* Screen Share Indicator */
+.screen-share-indicator {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ background: rgba(114, 137, 218, 0.9);
+ color: white;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ font-size: 0.875rem;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ z-index: 10;
+}
+
+.screen-share-indicator::before {
+ content: '🖥️';
+}
+
+/* Participant Name Label */
+.participant-name {
+ position: absolute;
+ bottom: 0.5rem;
+ left: 0.5rem;
+ background: rgba(0, 0, 0, 0.6);
+ color: white;
+ padding: 0.25rem 0.75rem;
+ border-radius: 3px;
+ font-size: 0.8125rem;
+ font-weight: 500;
+ z-index: 5;
+}
diff --git a/astro-site/src/react-app/components/Chat/VoiceVideoCall.jsx b/astro-site/src/react-app/components/Chat/VoiceVideoCall.jsx
new file mode 100644
index 0000000..8abe6cb
--- /dev/null
+++ b/astro-site/src/react-app/components/Chat/VoiceVideoCall.jsx
@@ -0,0 +1,129 @@
+/**
+ * VoiceVideoCall Component
+ * Displays voice/video call interface with participant videos and controls
+ */
+
+import React, { useEffect, useState } from 'react';
+import {
+ LiveKitRoom,
+ VideoConference,
+ GridLayout,
+ ParticipantTile,
+ useRemoteParticipant,
+ useLocalParticipant,
+} from 'livekit-react';
+import './VoiceVideoCall.css';
+
+export default function VoiceVideoCall({
+ roomName,
+ userName,
+ token,
+ serverUrl,
+ onLeave,
+}) {
+ const [callState, setCallState] = useState('connecting'); // connecting, connected, ended
+
+ if (!token || !serverUrl) {
+ return (
+
+
Missing required configuration for video call
+
+ );
+ }
+
+ return (
+
+
setCallState('connected')}
+ onDisconnected={() => {
+ setCallState('ended');
+ onLeave?.();
+ }}
+ options={{
+ adaptiveStream: true,
+ dynacast: true,
+ }}
+ >
+ {callState === 'connecting' && (
+
+
+
Connecting to {roomName}...
+
+ )}
+
+ {callState === 'connected' && (
+ <>
+
+
+ >
+ )}
+
+
+ );
+}
+
+/**
+ * CallControls Component
+ * Displays microphone, camera, screen share, and leave buttons
+ */
+function CallControls({ onLeave }) {
+ const { localParticipant } = useLocalParticipant();
+ const [isMuted, setIsMuted] = useState(false);
+ const [isCameraOff, setIsCameraOff] = useState(false);
+ const [isScreenSharing, setIsScreenSharing] = useState(false);
+
+ const handleToggleMicrophone = async () => {
+ await localParticipant?.setMicrophoneEnabled(isMuted);
+ setIsMuted(!isMuted);
+ };
+
+ const handleToggleCamera = async () => {
+ await localParticipant?.setCameraEnabled(isCameraOff);
+ setIsCameraOff(!isCameraOff);
+ };
+
+ const handleToggleScreenShare = async () => {
+ await localParticipant?.setScreenShareEnabled(!isScreenSharing);
+ setIsScreenSharing(!isScreenSharing);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/Voice/CallRoom.css b/astro-site/src/react-app/components/Voice/CallRoom.css
new file mode 100644
index 0000000..bf0cb02
--- /dev/null
+++ b/astro-site/src/react-app/components/Voice/CallRoom.css
@@ -0,0 +1,305 @@
+/**
+ * CallRoom CSS
+ * Voice/video call interface styling
+ */
+
+.call-room {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background: #0a0e17;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
+}
+
+.call-room.loading {
+ align-items: center;
+ justify-content: center;
+}
+
+.call-loading-content {
+ text-align: center;
+ color: #b5bac1;
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 4px solid rgba(0, 217, 255, 0.2);
+ border-top: 4px solid #00d9ff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto 1rem;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Call header */
+.call-header {
+ padding: 1rem;
+ background: rgba(0, 0, 0, 0.3);
+ border-bottom: 1px solid rgba(0, 217, 255, 0.1);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.call-info {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.call-info h2 {
+ margin: 0;
+ color: #ffffff;
+ font-size: 1.25rem;
+ font-weight: 600;
+}
+
+.participant-count {
+ background: rgba(0, 217, 255, 0.1);
+ color: #00d9ff;
+ padding: 0.25rem 0.75rem;
+ border-radius: 12px;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+
+.call-duration {
+ color: #72767d;
+ font-size: 0.9375rem;
+ font-weight: 500;
+ padding: 0.25rem 0.75rem;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 6px;
+}
+
+/* Video grid */
+.call-video-grid {
+ flex: 1;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 1rem;
+ padding: 1.5rem;
+ overflow-y: auto;
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.5) 0%, rgba(0, 217, 255, 0.05) 100%);
+}
+
+.participant-video {
+ position: relative;
+ background: rgba(0, 0, 0, 0.4);
+ border: 1px solid rgba(0, 217, 255, 0.2);
+ border-radius: 12px;
+ overflow: hidden;
+ aspect-ratio: 16 / 9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 250px;
+}
+
+.participant-video.empty {
+ grid-column: 1 / -1;
+ opacity: 0.5;
+}
+
+.participant-name {
+ position: absolute;
+ top: 0.75rem;
+ left: 0.75rem;
+ background: rgba(0, 0, 0, 0.6);
+ color: #ffffff;
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ z-index: 10;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.video-placeholder {
+ color: #72767d;
+ text-align: center;
+ padding: 1rem;
+}
+
+/* Control bar */
+.call-controls {
+ padding: 1rem;
+ background: rgba(0, 0, 0, 0.5);
+ border-top: 1px solid rgba(0, 217, 255, 0.1);
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.control-btn {
+ padding: 0.75rem 1.5rem;
+ border: 1px solid rgba(0, 217, 255, 0.3);
+ background: rgba(0, 217, 255, 0.1);
+ color: #00d9ff;
+ border-radius: 8px;
+ font-size: 0.9375rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.control-btn:hover {
+ background: rgba(0, 217, 255, 0.2);
+ border-color: rgba(0, 217, 255, 0.5);
+ transform: translateY(-2px);
+}
+
+.control-btn.active {
+ background: rgba(114, 137, 218, 0.3);
+ border-color: #7289da;
+ color: #7289da;
+}
+
+.control-btn.inactive {
+ background: rgba(244, 54, 54, 0.1);
+ border-color: rgba(244, 54, 54, 0.3);
+ color: #f43636;
+}
+
+.control-btn.end-call {
+ background: rgba(244, 54, 54, 0.2);
+ border-color: #f43636;
+ color: #ffffff;
+}
+
+.control-btn.end-call:hover {
+ background: rgba(244, 54, 54, 0.3);
+ box-shadow: 0 0 16px rgba(244, 54, 54, 0.3);
+}
+
+/* Error message */
+.call-error {
+ position: fixed;
+ bottom: 1rem;
+ right: 1rem;
+ background: rgba(244, 54, 54, 0.1);
+ border: 1px solid #f43636;
+ border-radius: 8px;
+ padding: 1rem;
+ color: #ffffff;
+ z-index: 1000;
+ max-width: 400px;
+ animation: slideIn 0.3s ease-out;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateY(100px);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+.call-error p {
+ margin: 0 0 0.75rem 0;
+ color: #f43636;
+}
+
+.call-error button {
+ background: #f43636;
+ color: white;
+ border: none;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.875rem;
+ transition: background 0.2s;
+}
+
+.call-error button:hover {
+ background: #d63030;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .call-video-grid {
+ grid-template-columns: 1fr;
+ padding: 1rem;
+ gap: 0.75rem;
+ }
+
+ .call-info {
+ flex-direction: column;
+ gap: 0.5rem;
+ align-items: flex-start;
+ }
+
+ .call-info h2 {
+ font-size: 1.125rem;
+ }
+
+ .control-btn {
+ padding: 0.625rem 1rem;
+ font-size: 0.8125rem;
+ }
+
+ .call-controls {
+ padding: 0.75rem;
+ gap: 0.5rem;
+ }
+
+ .participant-video {
+ min-height: 200px;
+ }
+}
+
+@media (max-width: 480px) {
+ .call-room {
+ border-radius: 0;
+ }
+
+ .call-info {
+ gap: 0.5rem;
+ }
+
+ .call-info h2 {
+ font-size: 1rem;
+ }
+
+ .participant-count,
+ .call-duration {
+ font-size: 0.75rem;
+ padding: 0.25rem 0.5rem;
+ }
+
+ .control-btn {
+ padding: 0.5rem 0.75rem;
+ font-size: 0.75rem;
+ }
+
+ .participant-video {
+ min-height: 150px;
+ }
+
+ .call-error {
+ bottom: 0.5rem;
+ right: 0.5rem;
+ left: 0.5rem;
+ max-width: 100%;
+ }
+}
diff --git a/astro-site/src/react-app/components/Voice/CallRoom.jsx b/astro-site/src/react-app/components/Voice/CallRoom.jsx
new file mode 100644
index 0000000..bd3af24
--- /dev/null
+++ b/astro-site/src/react-app/components/Voice/CallRoom.jsx
@@ -0,0 +1,210 @@
+/**
+ * CallRoom Component
+ * Voice/video call interface using LiveKit
+ */
+
+import React, { useEffect, useState, useCallback } from 'react';
+import liveKitService from '../../services/livekitService';
+import './CallRoom.css';
+
+export default function CallRoom({ token, url, roomName, onClose }) {
+ const [connected, setConnected] = useState(false);
+ const [audioEnabled, setAudioEnabled] = useState(true);
+ const [videoEnabled, setVideoEnabled] = useState(true);
+ const [screenShareEnabled, setScreenShareEnabled] = useState(false);
+ const [participants, setParticipants] = useState([]);
+ const [error, setError] = useState(null);
+ const [callDuration, setCallDuration] = useState(0);
+
+ // Initialize room connection
+ useEffect(() => {
+ const initializeRoom = async () => {
+ try {
+ await liveKitService.connect(url, token, roomName);
+ setConnected(true);
+ updateParticipants();
+
+ // Setup event listeners
+ liveKitService.on('participant_joined', () => updateParticipants());
+ liveKitService.on('participant_left', () => updateParticipants());
+ liveKitService.on('connection_error', (err) => {
+ console.error('Connection error:', err);
+ setError(err.message);
+ });
+ } catch (err) {
+ console.error('Failed to initialize room:', err);
+ setError('Failed to join call');
+ }
+ };
+
+ if (token && url && roomName) {
+ initializeRoom();
+ }
+
+ return () => {
+ liveKitService.off('participant_joined', updateParticipants);
+ liveKitService.off('participant_left', updateParticipants);
+ };
+ }, [token, url, roomName]);
+
+ // Call duration timer
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setCallDuration((prev) => prev + 1);
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ // Update participants list
+ const updateParticipants = useCallback(() => {
+ const allParticipants = liveKitService.getAllParticipants();
+ setParticipants(allParticipants);
+ }, []);
+
+ // Toggle audio
+ const handleToggleAudio = async () => {
+ try {
+ await liveKitService.toggleAudio(!audioEnabled);
+ setAudioEnabled(!audioEnabled);
+ } catch (error) {
+ console.error('Failed to toggle audio:', error);
+ setError('Failed to toggle audio');
+ }
+ };
+
+ // Toggle video
+ const handleToggleVideo = async () => {
+ try {
+ await liveKitService.toggleVideo(!videoEnabled);
+ setVideoEnabled(!videoEnabled);
+ } catch (error) {
+ console.error('Failed to toggle video:', error);
+ setError('Failed to toggle video');
+ }
+ };
+
+ // Toggle screen share
+ const handleToggleScreenShare = async () => {
+ try {
+ await liveKitService.toggleScreenShare(!screenShareEnabled);
+ setScreenShareEnabled(!screenShareEnabled);
+ } catch (error) {
+ console.error('Failed to toggle screen share:', error);
+ setError('Failed to toggle screen share');
+ }
+ };
+
+ // End call
+ const handleEndCall = async () => {
+ try {
+ await liveKitService.disconnect();
+ onClose?.();
+ } catch (error) {
+ console.error('Failed to end call:', error);
+ }
+ };
+
+ // Format call duration
+ const formatDuration = (seconds) => {
+ const hours = Math.floor(seconds / 3600);
+ const minutes = Math.floor((seconds % 3600) / 60);
+ const secs = seconds % 60;
+
+ if (hours > 0) {
+ return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
+ }
+ return `${minutes}:${String(secs).padStart(2, '0')}`;
+ };
+
+ if (!connected) {
+ return (
+
+
+
+
Connecting to call...
+ {error &&
{error}
}
+
+
+ );
+ }
+
+ return (
+
+ {/* Call header */}
+
+
+
{roomName}
+ {participants.length} participant(s)
+ {formatDuration(callDuration)}
+
+
+
+ {/* Video grid */}
+
+ {participants.map((participant) => (
+
+
+ {participant.identity || 'Unknown'}
+
+
+ 📹 {participant.identity}
+
+
+ ))}
+
+ {/* Empty state */}
+ {participants.length === 0 && (
+
+
+ Waiting for participants...
+
+
+ )}
+
+
+ {/* Control bar */}
+
+
+
+
+
+
+
+
+
+
+ {/* Error message */}
+ {error && (
+
+
{error}
+
+
+ )}
+
+ );
+}
diff --git a/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.css b/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.css
new file mode 100644
index 0000000..f8f52a7
--- /dev/null
+++ b/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.css
@@ -0,0 +1,219 @@
+/**
+ * VoiceCallModal CSS
+ */
+
+.voice-call-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 2000;
+ animation: fadeIn 0.2s ease-in-out;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.voice-call-modal {
+ background: #2c2f33;
+ border-radius: 12px;
+ width: 90%;
+ max-width: 500px;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
+ overflow: hidden;
+ animation: slideUp 0.3s ease-out;
+}
+
+@keyframes slideUp {
+ from {
+ transform: translateY(20px);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+.modal-header {
+ padding: 1.5rem;
+ border-bottom: 1px solid #23272a;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: #23272a;
+}
+
+.modal-header h2 {
+ margin: 0;
+ font-size: 1.25rem;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.modal-close {
+ background: none;
+ border: none;
+ color: #72767d;
+ font-size: 1.5rem;
+ cursor: pointer;
+ transition: color 0.2s;
+ padding: 0;
+ width: 32px;
+ height: 32px;
+}
+
+.modal-close:hover {
+ color: #ffffff;
+}
+
+.modal-content {
+ flex: 1;
+ padding: 2rem 1.5rem;
+ overflow-y: auto;
+}
+
+.call-info {
+ margin-bottom: 1.5rem;
+}
+
+.info-label {
+ font-size: 0.8125rem;
+ text-transform: uppercase;
+ color: #72767d;
+ margin: 0 0 0.5rem 0;
+ letter-spacing: 0.5px;
+ font-weight: 600;
+}
+
+.info-value {
+ font-size: 1rem;
+ color: #ffffff;
+ margin: 0;
+}
+
+.error-message {
+ background: rgba(244, 54, 54, 0.1);
+ border: 1px solid #f43636;
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1.5rem;
+ display: flex;
+ gap: 0.75rem;
+ color: #f43636;
+}
+
+.error-message span {
+ font-size: 1.25rem;
+ flex-shrink: 0;
+}
+
+.error-message p {
+ margin: 0;
+ flex: 1;
+}
+
+.call-features {
+ background: rgba(114, 137, 218, 0.1);
+ border-left: 3px solid #7289da;
+ border-radius: 4px;
+ padding: 1rem;
+}
+
+.call-features h3 {
+ margin: 0 0 0.75rem 0;
+ font-size: 0.9375rem;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.call-features ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.call-features li {
+ font-size: 0.875rem;
+ color: #b5bac1;
+}
+
+.modal-footer {
+ padding: 1rem 1.5rem;
+ border-top: 1px solid #23272a;
+ display: flex;
+ gap: 1rem;
+ justify-content: flex-end;
+ background: #2c2f33;
+}
+
+.btn-cancel {
+ padding: 0.75rem 1.5rem;
+ background: #424549;
+ color: #ffffff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: background 0.2s;
+}
+
+.btn-cancel:hover {
+ background: #36393f;
+}
+
+.btn-call {
+ padding: 0.75rem 1.5rem;
+ background: linear-gradient(135deg, #7289da 0%, #00d9ff 100%);
+ color: #ffffff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s;
+}
+
+.btn-call:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(114, 137, 218, 0.3);
+}
+
+.btn-call:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+@media (max-width: 480px) {
+ .voice-call-modal {
+ width: 95%;
+ }
+
+ .modal-content {
+ padding: 1.5rem 1rem;
+ }
+
+ .modal-footer {
+ flex-direction: column;
+ }
+
+ .btn-cancel,
+ .btn-call {
+ width: 100%;
+ }
+}
diff --git a/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.jsx b/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.jsx
new file mode 100644
index 0000000..a62db8d
--- /dev/null
+++ b/astro-site/src/react-app/components/VoiceCall/VoiceCallModal.jsx
@@ -0,0 +1,140 @@
+/**
+ * VoiceCallModal Component
+ * Modal for initiating and managing voice/video calls
+ */
+
+import React, { useState, useEffect } from 'react';
+import VoiceCallUI from './VoiceCallUI';
+import './VoiceCallModal.css';
+
+export default function VoiceCallModal({
+ isOpen,
+ onClose,
+ channelId,
+ channelName,
+ userId,
+ userName,
+}) {
+ const [inCall, setInCall] = useState(false);
+ const [token, setToken] = useState(null);
+ const [serverUrl, setServerUrl] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (!isOpen || !channelId) {
+ setInCall(false);
+ setToken(null);
+ return;
+ }
+ }, [isOpen, channelId]);
+
+ const handleStartCall = async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000';
+
+ // Request LiveKit token from backend
+ const response = await fetch(`${apiUrl}/api/calls/token`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${localStorage.getItem('token')}`,
+ },
+ body: JSON.stringify({
+ room: `channel-${channelId}`,
+ user_name: userName,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to get call token');
+ }
+
+ const data = await response.json();
+ setToken(data.token);
+ setServerUrl(data.serverUrl);
+ setInCall(true);
+ } catch (err) {
+ setError(err.message);
+ console.error('Error starting call:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleEndCall = () => {
+ setInCall(false);
+ setToken(null);
+ onClose();
+ };
+
+ if (!isOpen) return null;
+
+ if (inCall && token && serverUrl) {
+ return (
+
+ );
+ }
+
+ return (
+
+
e.stopPropagation()}>
+
+
Start Voice Call
+
+
+
+
+
+
Channel
+
{channelName}
+
+
+
+
Participant
+
{userName}
+
+
+ {error && (
+
+ )}
+
+
+
Features
+
+ - ✓ Crystal-clear audio and HD video
+ - ✓ Screen sharing
+ - ✓ Multi-participant support
+ - ✓ Automatic recording (optional)
+ - ✓ Low-latency performance
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.css b/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.css
new file mode 100644
index 0000000..264ee66
--- /dev/null
+++ b/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.css
@@ -0,0 +1,187 @@
+/**
+ * VoiceCallUI CSS
+ * Video call interface styling
+ */
+
+.voice-call-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #0a0a0f;
+ display: flex;
+ flex-direction: column;
+ z-index: 3000;
+ animation: slideIn 0.3s ease-out;
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+.call-header {
+ padding: 1rem 1.5rem;
+ background: rgba(23, 39, 58, 0.8);
+ border-bottom: 1px solid rgba(0, 217, 255, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.call-header h3 {
+ margin: 0;
+ color: #ffffff;
+ font-size: 1.25rem;
+ font-weight: 600;
+}
+
+.call-stats {
+ display: flex;
+ gap: 1.5rem;
+ color: #b5bac1;
+ font-size: 0.9375rem;
+}
+
+.video-grid {
+ flex: 1;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 1rem;
+ padding: 1rem;
+ overflow: auto;
+}
+
+.video-grid :global(.lk-grid-layout) {
+ width: 100%;
+ height: 100%;
+}
+
+.video-grid :global(.lk-participant-tile) {
+ aspect-ratio: 16 / 9;
+ border-radius: 8px;
+ overflow: hidden;
+ background: #1a1d1f;
+}
+
+.call-controls {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 1rem;
+ padding: 1.5rem;
+ background: rgba(23, 39, 58, 0.8);
+ border-top: 1px solid rgba(0, 217, 255, 0.1);
+}
+
+.control-btn {
+ width: 56px;
+ height: 56px;
+ border-radius: 50%;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(0, 217, 255, 0.1);
+ color: #00d9ff;
+}
+
+.control-btn:hover {
+ background: rgba(0, 217, 255, 0.2);
+ transform: scale(1.1);
+}
+
+.control-btn.active {
+ background: rgba(0, 217, 255, 0.3);
+ box-shadow: 0 0 0 3px rgba(0, 217, 255, 0.2);
+}
+
+.control-btn.inactive {
+ background: rgba(244, 54, 54, 0.2);
+ color: #f43636;
+}
+
+.control-btn.end-call {
+ background: rgba(244, 54, 54, 0.2);
+ color: #f43636;
+ font-size: 1.75rem;
+}
+
+.control-btn.end-call:hover {
+ background: rgba(244, 54, 54, 0.3);
+ transform: scale(1.1);
+}
+
+.call-error {
+ position: absolute;
+ bottom: 80px;
+ left: 50%;
+ transform: translateX(-50%);
+ background: rgba(244, 54, 54, 0.1);
+ border: 1px solid #f43636;
+ border-radius: 8px;
+ padding: 1rem 1.5rem;
+ color: #f43636;
+ max-width: 400px;
+ z-index: 100;
+}
+
+.voice-call-error {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ background: #0a0a0f;
+ color: #b5bac1;
+ gap: 1rem;
+}
+
+.voice-call-error button {
+ padding: 0.75rem 1.5rem;
+ background: #7289da;
+ color: white;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: background 0.2s;
+}
+
+.voice-call-error button:hover {
+ background: #5a6fc1;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .video-grid {
+ grid-template-columns: 1fr;
+ padding: 0.5rem;
+ gap: 0.5rem;
+ }
+
+ .control-btn {
+ width: 48px;
+ height: 48px;
+ font-size: 1.25rem;
+ }
+
+ .call-header {
+ padding: 0.75rem 1rem;
+ }
+
+ .call-controls {
+ padding: 1rem;
+ gap: 0.75rem;
+ }
+}
diff --git a/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.jsx b/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.jsx
new file mode 100644
index 0000000..10c605b
--- /dev/null
+++ b/astro-site/src/react-app/components/VoiceCall/VoiceCallUI.jsx
@@ -0,0 +1,117 @@
+/**
+ * VoiceCallUI Component
+ * Voice/video call interface with LiveKit
+ */
+
+import React, { useState, useEffect } from 'react';
+import {
+ LiveKitRoom,
+ VideoConference,
+ GridLayout,
+ ParticipantTile,
+ RoomAudioRenderer,
+ useLocalParticipant,
+ useRemoteParticipants,
+} from 'livekit-react';
+import './VoiceCallUI.css';
+
+export default function VoiceCallUI({ token, serverUrl, roomName, onClose }) {
+ const [videoEnabled, setVideoEnabled] = useState(true);
+ const [audioEnabled, setAudioEnabled] = useState(true);
+ const [screenSharing, setScreenSharing] = useState(false);
+ const [error, setError] = useState(null);
+
+ const localParticipant = useLocalParticipant();
+ const remoteParticipants = useRemoteParticipants();
+
+ if (!token || !serverUrl) {
+ return (
+
+
Missing LiveKit credentials
+
+
+ );
+ }
+
+ const handleScreenShare = async () => {
+ try {
+ if (!screenSharing) {
+ await localParticipant?.localParticipant?.setScreenShareEnabled(true);
+ setScreenSharing(true);
+ } else {
+ await localParticipant?.localParticipant?.setScreenShareEnabled(false);
+ setScreenSharing(false);
+ }
+ } catch (err) {
+ setError('Failed to toggle screen share');
+ console.error(err);
+ }
+ };
+
+ return (
+
+
setError(err.message)}
+ >
+
+
{roomName}
+
+ {remoteParticipants.length + 1} participants
+
+
+
+
+ p.videoTrack)]}>
+ {/* Video tiles rendered automatically */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {error && (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/astro-site/src/react-app/hooks/useLiveKit.js b/astro-site/src/react-app/hooks/useLiveKit.js
new file mode 100644
index 0000000..73faf37
--- /dev/null
+++ b/astro-site/src/react-app/hooks/useLiveKit.js
@@ -0,0 +1,89 @@
+/**
+ * useLiveKit - Hook for LiveKit room management
+ * Handles room connection, participant tracking, and media controls
+ */
+
+import { useEffect, useRef, useCallback, useState } from 'react';
+import { useRoom, useParticipants, useLocalParticipant } from '@livekit/react';
+
+export function useLiveKit(roomName, userName, token) {
+ const { room, isConnecting, isConnected } = useRoom();
+ const participants = useParticipants();
+ const { localParticipant } = useLocalParticipant();
+
+ const [isMuted, setIsMuted] = useState(false);
+ const [isCameraOff, setIsCameraOff] = useState(false);
+ const [isScreenSharing, setIsScreenSharing] = useState(false);
+ const trackSubscriptionRef = useRef([]);
+
+ // Toggle microphone
+ const toggleMicrophone = useCallback(async () => {
+ if (localParticipant) {
+ await localParticipant.setMicrophoneEnabled(!isMuted);
+ setIsMuted(!isMuted);
+ }
+ }, [isMuted, localParticipant]);
+
+ // Toggle camera
+ const toggleCamera = useCallback(async () => {
+ if (localParticipant) {
+ await localParticipant.setCameraEnabled(isCameraOff);
+ setIsCameraOff(!isCameraOff);
+ }
+ }, [isCameraOff, localParticipant]);
+
+ // Start screen share
+ const startScreenShare = useCallback(async () => {
+ try {
+ if (localParticipant) {
+ await localParticipant.setScreenShareEnabled(true);
+ setIsScreenSharing(true);
+ }
+ } catch (error) {
+ console.error('Failed to start screen share:', error);
+ }
+ }, [localParticipant]);
+
+ // Stop screen share
+ const stopScreenShare = useCallback(async () => {
+ try {
+ if (localParticipant) {
+ await localParticipant.setScreenShareEnabled(false);
+ setIsScreenSharing(false);
+ }
+ } catch (error) {
+ console.error('Failed to stop screen share:', error);
+ }
+ }, [localParticipant]);
+
+ // Toggle screen share
+ const toggleScreenShare = useCallback(async () => {
+ if (isScreenSharing) {
+ await stopScreenShare();
+ } else {
+ await startScreenShare();
+ }
+ }, [isScreenSharing, startScreenShare, stopScreenShare]);
+
+ // Leave room
+ const leaveRoom = useCallback(async () => {
+ if (room) {
+ await room.disconnect();
+ }
+ }, [room]);
+
+ return {
+ room,
+ isConnecting,
+ isConnected,
+ participants,
+ localParticipant,
+ isMuted,
+ isCameraOff,
+ isScreenSharing,
+ toggleMicrophone,
+ toggleCamera,
+ toggleScreenShare,
+ leaveRoom,
+ };
+}
diff --git a/astro-site/src/react-app/hooks/useSocket.js b/astro-site/src/react-app/hooks/useSocket.js
new file mode 100644
index 0000000..d9ccffc
--- /dev/null
+++ b/astro-site/src/react-app/hooks/useSocket.js
@@ -0,0 +1,119 @@
+/**
+ * useSocket Hook
+ * Manages Socket.IO connection and message sync
+ */
+
+import { useEffect, useRef, useCallback } from 'react';
+import io from 'socket.io-client';
+
+export function useSocket(userId, token) {
+ const socketRef = useRef(null);
+ const isConnectedRef = useRef(false);
+
+ useEffect(() => {
+ if (!userId || !token) return;
+
+ const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000';
+
+ // Connect to Socket.IO server
+ socketRef.current = io(apiUrl, {
+ auth: { token },
+ reconnectionDelay: 1000,
+ reconnection: true,
+ reconnectionAttempts: 10,
+ });
+
+ // Connection events
+ socketRef.current.on('connect', () => {
+ console.log('Socket.IO connected:', socketRef.current.id);
+ isConnectedRef.current = true;
+ });
+
+ socketRef.current.on('disconnect', () => {
+ console.log('Socket.IO disconnected');
+ isConnectedRef.current = false;
+ });
+
+ socketRef.current.on('error', (error) => {
+ console.error('Socket.IO error:', error);
+ });
+
+ return () => {
+ if (socketRef.current) {
+ socketRef.current.disconnect();
+ }
+ };
+ }, [userId, token]);
+
+ const emit = useCallback((event, data) => {
+ if (socketRef.current && isConnectedRef.current) {
+ socketRef.current.emit(event, data);
+ }
+ }, []);
+
+ const on = useCallback((event, handler) => {
+ if (socketRef.current) {
+ socketRef.current.on(event, handler);
+ }
+ }, []);
+
+ const off = useCallback((event, handler) => {
+ if (socketRef.current) {
+ socketRef.current.off(event, handler);
+ }
+ }, []);
+
+ const joinChannel = useCallback((channelId) => {
+ emit('join_conversation', { conversation_id: channelId });
+ }, [emit]);
+
+ const leaveChannel = useCallback((channelId) => {
+ emit('leave_conversation', { conversation_id: channelId });
+ }, [emit]);
+
+ const sendMessage = useCallback((channelId, content, metadata = {}) => {
+ emit('message:send', {
+ conversation_id: channelId,
+ content,
+ metadata,
+ });
+ }, [emit]);
+
+ const editMessage = useCallback((messageId, content) => {
+ emit('message:edit', { id: messageId, content });
+ }, [emit]);
+
+ const deleteMessage = useCallback((messageId) => {
+ emit('message:delete', { id: messageId });
+ }, [emit]);
+
+ const addReaction = useCallback((messageId, emoji) => {
+ emit('message:reaction', { id: messageId, emoji });
+ }, [emit]);
+
+ const startTyping = useCallback((channelId) => {
+ emit('typing:start', { conversation_id: channelId });
+ }, [emit]);
+
+ const stopTyping = useCallback((channelId) => {
+ emit('typing:stop', { conversation_id: channelId });
+ }, [emit]);
+
+ return {
+ socket: socketRef.current,
+ isConnected: isConnectedRef.current,
+ emit,
+ on,
+ off,
+ joinChannel,
+ leaveChannel,
+ sendMessage,
+ editMessage,
+ deleteMessage,
+ addReaction,
+ startTyping,
+ stopTyping,
+ };
+}
+
+export default useSocket;
diff --git a/astro-site/src/react-app/hooks/useSocketEvents.js b/astro-site/src/react-app/hooks/useSocketEvents.js
new file mode 100644
index 0000000..792657e
--- /dev/null
+++ b/astro-site/src/react-app/hooks/useSocketEvents.js
@@ -0,0 +1,56 @@
+/**
+ * useSocketEvents Hook
+ * Manages Socket.IO real-time event handling for messages
+ */
+
+import { useEffect } from 'react';
+import { useSocket } from './useSocket';
+import { messageStore } from '../stores/messageStore';
+
+export function useSocketEvents(token, channelId) {
+ const { socket } = useSocket(token);
+
+ useEffect(() => {
+ if (!socket || !channelId) return;
+
+ // New message received
+ const handleNewMessage = (message) => {
+ messageStore.addMessage(message);
+ };
+
+ // Message edited
+ const handleMessageUpdated = (data) => {
+ messageStore.updateMessage(data.id, { content: data.content, editedAt: data.editedAt });
+ };
+
+ // Message deleted
+ const handleMessageDeleted = (data) => {
+ messageStore.deleteMessage(data.id);
+ };
+
+ // Message reactions updated
+ const handleReactionsUpdated = (data) => {
+ messageStore.updateMessage(data.id, { reactions: data.reactions });
+ };
+
+ // Register listeners
+ socket.on('message:new', handleNewMessage);
+ socket.on('message:updated', handleMessageUpdated);
+ socket.on('message:deleted', handleMessageDeleted);
+ socket.on('message:reactions_updated', handleReactionsUpdated);
+
+ // Join channel
+ socket.emit('join_conversation', { conversationId: channelId });
+
+ // Cleanup
+ return () => {
+ socket.off('message:new', handleNewMessage);
+ socket.off('message:updated', handleMessageUpdated);
+ socket.off('message:deleted', handleMessageDeleted);
+ socket.off('message:reactions_updated', handleReactionsUpdated);
+ socket.emit('leave_conversation', { conversationId: channelId });
+ };
+ }, [socket, channelId]);
+
+ return socket;
+}
diff --git a/astro-site/src/react-app/mockup/global.css b/astro-site/src/react-app/mockup/global.css
index b514874..08e9f90 100644
--- a/astro-site/src/react-app/mockup/global.css
+++ b/astro-site/src/react-app/mockup/global.css
@@ -1,5 +1,11 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;700&display=swap');
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
html, body, #root {
height: 100%;
margin: 0;
@@ -7,15 +13,17 @@ html, body, #root {
font-family: 'Roboto Mono', monospace;
background: #0a0a0a;
color: #e0e0e0;
+ overflow: hidden;
}
+/* Scanline effect */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
- width: 100vw;
- height: 100vh;
+ width: 100%;
+ height: 100%;
background: repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.15),
@@ -27,31 +35,754 @@ body::before {
z-index: 1000;
}
+/* Main Layout */
.connect-container {
- height: 100vh;
display: flex;
+ height: 100vh;
}
-.server-icon, .user-avatar, .member-avatar-small {
- background: rgba(26,26,26,0.85);
- backdrop-filter: blur(6px);
+/* Server Sidebar */
+.server-list {
+ width: 80px;
+ background: #0d0d0d;
+ border-right: 1px solid #1a1a1a;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 12px 0;
+ gap: 12px;
}
-.channel-item.active, .channel-item:hover, .member-item:hover {
- background: rgba(26,26,26,0.85);
- backdrop-filter: blur(4px);
+.server-icon {
+ width: 56px;
+ height: 56px;
+ background: #1a1a1a;
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 1.2em;
+ cursor: pointer;
+ transition: all 0.3s;
+ position: relative;
}
-.message-input, .message-input-container {
- background: rgba(15,15,15,0.95);
- backdrop-filter: blur(4px);
+.server-icon:hover {
+ border-radius: 12px;
+ transform: translateY(-2px);
}
+.server-icon.active {
+ border-radius: 12px;
+}
+
+.server-icon::before {
+ content: '';
+ position: absolute;
+ left: -12px;
+ width: 4px;
+ height: 0;
+ transition: height 0.3s;
+ border-radius: 0 4px 4px 0;
+}
+
+.server-icon.active::before {
+ height: 40px;
+}
+
+.server-icon.foundation {
+ background: linear-gradient(135deg, #ff0000 0%, #990000 100%);
+}
+
+.server-icon.foundation.active::before {
+ background: #ff0000;
+}
+
+.server-icon.corporation {
+ background: linear-gradient(135deg, #0066ff 0%, #003380 100%);
+}
+
+.server-icon.corporation.active::before {
+ background: #0066ff;
+}
+
+.server-icon.labs {
+ background: linear-gradient(135deg, #ffa500 0%, #ff8c00 100%);
+}
+
+.server-icon.labs.active::before {
+ background: #ffa500;
+}
+
+.server-icon.community {
+ background: #1a1a1a;
+ color: #666;
+}
+
+.server-divider {
+ width: 40px;
+ height: 2px;
+ background: #1a1a1a;
+ margin: 4px 0;
+}
+
+/* Channel Sidebar */
+.channel-sidebar {
+ width: 280px;
+ background: #0f0f0f;
+ border-right: 1px solid #1a1a1a;
+ display: flex;
+ flex-direction: column;
+}
+
+.server-header {
+ padding: 16px;
+ border-bottom: 1px solid #1a1a1a;
+ font-weight: 700;
+ font-size: 1.1em;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.server-badge {
+ font-size: 0.7em;
+ padding: 4px 8px;
+ border-radius: 4px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+.server-badge.foundation {
+ background: rgba(255, 0, 0, 0.2);
+ color: #ff0000;
+ border: 1px solid #ff0000;
+}
+
+.server-badge.corporation {
+ background: rgba(0, 102, 255, 0.2);
+ color: #0066ff;
+ border: 1px solid #0066ff;
+}
+
+.server-badge.labs {
+ background: rgba(255, 165, 0, 0.2);
+ color: #ffa500;
+ border: 1px solid #ffa500;
+}
+
+.channel-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px 0;
+}
+
+.channel-category {
+ padding: 16px 16px 8px 16px;
+ font-size: 0.75em;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: #666;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.add-channel-button {
+ opacity: 0;
+ transition: opacity 0.2s, color 0.2s;
+ cursor: pointer;
+ background: none;
+ border: none;
+ color: #666;
+ padding: 2px;
+ display: flex;
+ align-items: center;
+}
+
+.channel-category:hover .add-channel-button {
+ opacity: 1;
+}
+
+.add-channel-button:hover {
+ color: #999;
+}
+
+.channel-item {
+ padding: 8px 16px;
+ margin: 2px 8px;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 0.95em;
+ color: #999;
+}
+
+.channel-item:hover {
+ background: #1a1a1a;
+ color: #e0e0e0;
+}
+
+.channel-item.active {
+ background: #1a1a1a;
+ color: #0066ff;
+}
+
+.channel-icon {
+ color: #666;
+}
+
+.channel-name {
+ flex: 1;
+}
+
+.channel-badge {
+ font-size: 0.75em;
+ background: #ff0000;
+ color: #fff;
+ padding: 2px 6px;
+ border-radius: 10px;
+}
+
+/* User Presence Panel */
+.user-presence {
+ padding: 12px 16px;
+ border-top: 1px solid #1a1a1a;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ font-size: 0.9em;
+}
+
+.user-avatar {
+ width: 40px;
+ height: 40px;
+ background: linear-gradient(135deg, #ff0000, #0066ff, #ffa500);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+}
+
+.user-info {
+ flex: 1;
+}
+
+.user-name {
+ font-weight: 700;
+ margin-bottom: 2px;
+}
+
+.user-status {
+ font-size: 0.85em;
+ color: #666;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #00ff00;
+ box-shadow: 0 0 8px #00ff00;
+}
+
+/* Chat Area */
+.chat-area {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: #0a0a0a;
+}
+
+.chat-header {
+ padding: 16px 20px;
+ border-bottom: 1px solid #1a1a1a;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.channel-name-header {
+ flex: 1;
+ font-weight: 700;
+ font-size: 1.1em;
+}
+
+.chat-tools {
+ display: flex;
+ gap: 16px;
+ font-size: 0.9em;
+ color: #666;
+ align-items: center;
+}
+
+.chat-tool {
+ cursor: pointer;
+ transition: color 0.2s;
+}
+
+.chat-tool:hover {
+ color: #0066ff;
+}
+
+.chat-messages {
+ flex: 1;
+ overflow-y: auto;
+ padding: 20px;
+}
+
+.empty-state {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: #666;
+}
+
+.message {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 20px;
+ padding: 12px;
+ border-radius: 4px;
+ transition: background 0.2s;
+ position: relative;
+}
+
+.message:hover {
+ background: #0f0f0f;
+}
+
+.message-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: #1a1a1a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 0.9em;
+ flex-shrink: 0;
+}
+
+.message-content {
+ flex: 1;
+}
+
+.message-header {
+ display: flex;
+ align-items: baseline;
+ gap: 12px;
+ margin-bottom: 4px;
+}
+
+.message-author {
+ font-weight: 700;
+}
+
+.message-time {
+ font-size: 0.75em;
+ color: #666;
+}
+
+.message-edited {
+ font-size: 0.75em;
+ color: #666;
+ font-style: italic;
+}
+
+.message-badge {
+ font-size: 0.65em;
+ padding: 2px 6px;
+ border-radius: 3px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-weight: 700;
+}
+
+.message-badge.foundation {
+ background: rgba(255, 0, 0, 0.2);
+ color: #ff0000;
+}
+
+.message-badge.corporation {
+ background: rgba(0, 102, 255, 0.2);
+ color: #0066ff;
+}
+
+.message-badge.labs {
+ background: rgba(255, 165, 0, 0.2);
+ color: #ffa500;
+}
+
+.message-text {
+ line-height: 1.6;
+ color: #ccc;
+}
+
+.message-actions {
+ display: flex;
+ gap: 8px;
+ opacity: 0;
+ transition: opacity 0.2s;
+ position: absolute;
+ top: 12px;
+ right: 12px;
+}
+
+.message:hover .message-actions {
+ opacity: 1;
+}
+
+.message-action-edit,
+.message-action-delete {
+ padding: 4px;
+ border-radius: 4px;
+ background: none;
+ border: none;
+ color: #666;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.message-action-edit:hover {
+ background: rgba(0, 102, 255, 0.2);
+ color: #0066ff;
+}
+
+.message-action-delete:hover {
+ background: rgba(255, 0, 0, 0.2);
+ color: #ff0000;
+}
+
+.message-edit {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+.message-edit-input {
+ flex: 1;
+ background: #1a1a1a;
+ border: 1px solid #0066ff;
+ border-radius: 4px;
+ padding: 6px 12px;
+ color: #e0e0e0;
+ font-family: 'Roboto Mono', monospace;
+ font-size: 0.9em;
+}
+
+.message-edit-input:focus {
+ outline: none;
+}
+
+.message-edit-save,
+.message-edit-cancel {
+ padding: 6px 12px;
+ border-radius: 4px;
+ border: none;
+ font-family: 'Roboto Mono', monospace;
+ font-size: 0.8em;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.message-edit-save {
+ background: #0066ff;
+ color: #fff;
+}
+
+.message-edit-save:hover {
+ background: #0052cc;
+}
+
+.message-edit-cancel {
+ background: #666;
+ color: #fff;
+}
+
+.message-edit-cancel:hover {
+ background: #555;
+}
+
+.message-system {
+ background: #0f0f0f;
+ border-left: 3px solid;
+ padding: 12px;
+ margin-bottom: 16px;
+ font-size: 0.9em;
+}
+
+.message-system.foundation {
+ border-color: #ff0000;
+}
+
+.message-system.corporation {
+ border-color: #0066ff;
+}
+
+.message-system.labs {
+ border-color: #ffa500;
+}
+
+.system-label {
+ font-size: 0.75em;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ margin-bottom: 6px;
+ font-weight: 700;
+}
+
+.system-label.foundation { color: #ff0000; }
+.system-label.corporation { color: #0066ff; }
+.system-label.labs { color: #ffa500; }
+
+/* Message Input */
+.message-input-container {
+ padding: 20px;
+ border-top: 1px solid #1a1a1a;
+}
+
+.message-input-form {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.attach-button,
+.send-button {
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ border: none;
+ background: #1a1a1a;
+ color: #666;
+ font-size: 1.2em;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attach-button:hover {
+ background: #2a2a2a;
+ color: #999;
+}
+
+.send-button {
+ background: #0066ff;
+ color: #fff;
+}
+
+.send-button:hover {
+ background: #0052cc;
+}
+
+.send-button:disabled {
+ background: #2a2a2a;
+ color: #666;
+ cursor: not-allowed;
+}
+
+.message-input {
+ flex: 1;
+ background: #0f0f0f;
+ border: 1px solid #1a1a1a;
+ border-radius: 8px;
+ padding: 12px 16px;
+ color: #e0e0e0;
+ font-family: 'Roboto Mono', monospace;
+ font-size: 0.95em;
+ transition: border-color 0.3s;
+}
+
+.message-input:focus {
+ outline: none;
+ border-color: #0066ff;
+}
+
+.message-input::placeholder {
+ color: #666;
+}
+
+.typing-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px;
+ color: #666;
+ font-size: 0.85em;
+}
+
+.typing-dots {
+ display: flex;
+ gap: 4px;
+}
+
+.dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #666;
+ animation: bounce 1.4s infinite ease-in-out both;
+}
+
+@keyframes bounce {
+ 0%, 80%, 100% {
+ transform: scale(0);
+ opacity: 0.5;
+ }
+ 40% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+/* Member Sidebar */
+.member-sidebar {
+ width: 280px;
+ background: #0f0f0f;
+ border-left: 1px solid #1a1a1a;
+ display: flex;
+ flex-direction: column;
+}
+
+.member-header {
+ padding: 16px;
+ border-bottom: 1px solid #1a1a1a;
+ font-size: 0.85em;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: #666;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.manage-members-button {
+ padding: 4px;
+ border-radius: 4px;
+ background: none;
+ border: none;
+ color: #666;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+}
+
+.manage-members-button:hover {
+ background: #1a1a1a;
+ color: #999;
+}
+
+.member-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px 0;
+}
+
+.member-section {
+ margin-bottom: 16px;
+}
+
+.member-section-title {
+ padding: 8px 16px;
+ font-size: 0.75em;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ color: #666;
+ font-weight: 700;
+}
+
+.member-item {
+ padding: 6px 16px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.member-item:hover {
+ background: #1a1a1a;
+}
+
+.member-item.voice-active {
+ background: rgba(0, 102, 255, 0.1);
+}
+
+.member-item.voice-peer {
+ background: rgba(0, 102, 255, 0.05);
+}
+
+.member-avatar-small {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: #1a1a1a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.8em;
+ font-weight: 700;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.online-indicator {
+ position: absolute;
+ bottom: -2px;
+ right: -2px;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ border: 2px solid #0f0f0f;
+}
+
+.online-indicator.online { background: #00ff00; }
+.online-indicator.in-game { background: #0066ff; }
+.online-indicator.labs { background: #ffa500; }
+.online-indicator.idle { background: #666; }
+
+.member-info {
+ flex: 1;
+}
+
+.member-name {
+ font-size: 0.9em;
+}
+
+.member-activity {
+ font-size: 0.75em;
+ color: #666;
+}
+
+/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
- background: #111;
+ background: #0a0a0a;
}
+
::-webkit-scrollbar-thumb {
- background: #222;
+ background: #1a1a1a;
border-radius: 4px;
}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #2a2a2a;
diff --git a/astro-site/src/react-app/services/apiService.js b/astro-site/src/react-app/services/apiService.js
new file mode 100644
index 0000000..d613190
--- /dev/null
+++ b/astro-site/src/react-app/services/apiService.js
@@ -0,0 +1,253 @@
+import axios from 'axios';
+
+const API_URL = import.meta.env?.VITE_API_URL || 'http://localhost:3000';
+const API_BASE = `${API_URL}/api`;
+
+// Create axios instance with default config
+const apiClient = axios.create({
+ baseURL: API_BASE,
+ timeout: 10000,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+});
+
+// Add auth interceptor
+apiClient.interceptors.request.use((config) => {
+ const token = localStorage.getItem('aethex_token');
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+});
+
+// Handle responses
+apiClient.interceptors.response.use(
+ (response) => response.data,
+ (error) => {
+ if (error.response?.status === 401) {
+ localStorage.removeItem('aethex_token');
+ window.location.href = '/login';
+ }
+ return Promise.reject(error);
+ }
+);
+
+// ============== AUTH ==============
+export const authService = {
+ login: (email, password) =>
+ apiClient.post('/auth/login', { email, password }),
+
+ register: (email, password, username, displayName) =>
+ apiClient.post('/auth/register', { email, password, username, displayName }),
+
+ demo: () =>
+ apiClient.post('/auth/demo'),
+
+ getMe: () =>
+ apiClient.get('/auth/me'),
+
+ logout: () => {
+ localStorage.removeItem('aethex_token');
+ },
+};
+
+// ============== SERVERS ==============
+export const serverService = {
+ list: () =>
+ apiClient.get('/servers'),
+
+ get: (serverId) =>
+ apiClient.get(`/servers/${serverId}`),
+
+ create: (data) =>
+ apiClient.post('/servers', data),
+
+ update: (serverId, data) =>
+ apiClient.put(`/servers/${serverId}`, data),
+
+ delete: (serverId) =>
+ apiClient.delete(`/servers/${serverId}`),
+
+ join: (serverId) =>
+ apiClient.post(`/servers/${serverId}/join`),
+
+ leave: (serverId) =>
+ apiClient.post(`/servers/${serverId}/leave`),
+
+ members: (serverId) =>
+ apiClient.get(`/servers/${serverId}/members`),
+
+ invite: (serverId, inviteCode) =>
+ apiClient.post(`/servers/${serverId}/invite`, { inviteCode }),
+};
+
+// ============== CHANNELS ==============
+export const channelService = {
+ list: (serverId) =>
+ apiClient.get(`/channels?serverId=${serverId}`),
+
+ get: (channelId) =>
+ apiClient.get(`/channels/${channelId}`),
+
+ create: (serverId, data) =>
+ apiClient.post('/channels', { ...data, serverId }),
+
+ update: (channelId, data) =>
+ apiClient.put(`/channels/${channelId}`, data),
+
+ delete: (channelId) =>
+ apiClient.delete(`/channels/${channelId}`),
+};
+
+// ============== MESSAGES ==============
+export const messageService = {
+ list: (channelId, limit = 50, offset = 0) =>
+ apiClient.get(`/messages?channelId=${channelId}&limit=${limit}&offset=${offset}`),
+
+ get: (messageId) =>
+ apiClient.get(`/messages/${messageId}`),
+
+ create: (channelId, content) =>
+ apiClient.post('/messages', { channelId, content }),
+
+ update: (messageId, content) =>
+ apiClient.put(`/messages/${messageId}`, { content }),
+
+ delete: (messageId) =>
+ apiClient.delete(`/messages/${messageId}`),
+
+ pin: (messageId) =>
+ apiClient.post(`/messages/${messageId}/pin`),
+
+ unpin: (messageId) =>
+ apiClient.post(`/messages/${messageId}/unpin`),
+
+ react: (messageId, emoji) =>
+ apiClient.post(`/messages/${messageId}/react`, { emoji }),
+
+ unreact: (messageId, emoji) =>
+ apiClient.delete(`/messages/${messageId}/react/${emoji}`),
+
+ search: (query, channelId) =>
+ apiClient.get(`/messages/search?q=${query}&channelId=${channelId}`),
+};
+
+// ============== DIRECT MESSAGES ==============
+export const directMessageService = {
+ list: () =>
+ apiClient.get('/direct-messages'),
+
+ conversations: () =>
+ apiClient.get('/direct-messages/conversations'),
+
+ getConversation: (userId) =>
+ apiClient.get(`/direct-messages/conversations/${userId}`),
+
+ messages: (conversationId, limit = 50, offset = 0) =>
+ apiClient.get(`/direct-messages/${conversationId}?limit=${limit}&offset=${offset}`),
+
+ send: (userId, content) =>
+ apiClient.post('/direct-messages', { userId, content }),
+};
+
+// ============== USERS ==============
+export const userService = {
+ get: (userId) =>
+ apiClient.get(`/users/${userId}`),
+
+ search: (query) =>
+ apiClient.get(`/users/search?q=${query}`),
+
+ getFriends: () =>
+ apiClient.get('/users/friends'),
+
+ getFriendRequests: () =>
+ apiClient.get('/users/friend-requests'),
+
+ sendFriendRequest: (userId) =>
+ apiClient.post('/users/friend-requests', { targetUserId: userId }),
+
+ acceptFriendRequest: (requestId) =>
+ apiClient.post(`/users/friend-requests/${requestId}/accept`),
+
+ rejectFriendRequest: (requestId) =>
+ apiClient.delete(`/users/friend-requests/${requestId}`),
+
+ removeFriend: (userId) =>
+ apiClient.delete(`/users/friends/${userId}`),
+
+ getBlockedUsers: () =>
+ apiClient.get('/users/blocked'),
+
+ blockUser: (userId) =>
+ apiClient.post('/users/block', { targetUserId: userId }),
+
+ unblockUser: (userId) =>
+ apiClient.delete(`/users/block/${userId}`),
+
+ updateProfile: (data) =>
+ apiClient.put('/users/profile', data),
+
+ updateStatus: (status) =>
+ apiClient.put('/users/status', { status }),
+};
+
+// ============== CALLS ==============
+export const callService = {
+ initiate: (userId) =>
+ apiClient.post('/calls', { userId }),
+
+ accept: (callId) =>
+ apiClient.post(`/calls/${callId}/accept`),
+
+ reject: (callId) =>
+ apiClient.post(`/calls/${callId}/reject`),
+
+ end: (callId) =>
+ apiClient.post(`/calls/${callId}/end`),
+
+ getActive: () =>
+ apiClient.get('/calls/active'),
+};
+
+// ============== NOTIFICATIONS ==============
+export const notificationService = {
+ list: () =>
+ apiClient.get('/notifications'),
+
+ markAsRead: (notificationId) =>
+ apiClient.put(`/notifications/${notificationId}/read`),
+
+ markAllAsRead: () =>
+ apiClient.post('/notifications/mark-all-read'),
+
+ clear: (notificationId) =>
+ apiClient.delete(`/notifications/${notificationId}`),
+
+ clearAll: () =>
+ apiClient.post('/notifications/clear-all'),
+};
+
+// ============== ADMIN ==============
+export const adminService = {
+ listUsers: () =>
+ apiClient.get('/admin/users'),
+
+ listServers: () =>
+ apiClient.get('/admin/servers'),
+
+ getStats: () =>
+ apiClient.get('/admin/stats'),
+
+ banUser: (userId, reason) =>
+ apiClient.post('/admin/users/ban', { userId, reason }),
+
+ unbanUser: (userId) =>
+ apiClient.post('/admin/users/unban', { userId }),
+
+ deleteServer: (serverId) =>
+ apiClient.delete(`/admin/servers/${serverId}`),
+};
+
+export default apiClient;
diff --git a/astro-site/src/react-app/services/livekit.js b/astro-site/src/react-app/services/livekit.js
new file mode 100644
index 0000000..ce9a702
--- /dev/null
+++ b/astro-site/src/react-app/services/livekit.js
@@ -0,0 +1,119 @@
+/**
+ * LiveKit Integration Service
+ * Handles voice/video calls with screen sharing support
+ */
+
+import { AccessToken } from 'livekit-server-sdk';
+
+/**
+ * Generate LiveKit token for user
+ * Called from backend
+ */
+export async function generateLiveKitToken(userId, userName, roomName) {
+ try {
+ const at = new AccessToken(
+ process.env.LIVEKIT_API_KEY,
+ process.env.LIVEKIT_API_SECRET
+ );
+
+ at.addGrant({
+ roomJoin: true,
+ room: roomName,
+ canPublish: true,
+ canPublishData: true,
+ canSubscribe: true,
+ canPublishSources: ['camera', 'microphone', 'screen_share'],
+ });
+
+ at.identity = userId;
+ at.name = userName;
+ at.metadata = JSON.stringify({
+ userId,
+ userName,
+ joinedAt: new Date().toISOString(),
+ });
+
+ return at.toJwt();
+ } catch (error) {
+ console.error('Error generating LiveKit token:', error);
+ throw error;
+ }
+}
+
+/**
+ * Get LiveKit connection URL
+ */
+export function getLiveKitUrl() {
+ return process.env.VITE_LIVEKIT_URL || process.env.LIVEKIT_WS_URL || 'ws://localhost:7880';
+}
+
+/**
+ * Create room on LiveKit server
+ */
+export async function createLiveKitRoom(roomName, maxParticipants = 10) {
+ try {
+ const response = await fetch(
+ `${process.env.VITE_LIVEKIT_API_URL}/twirp/livekit.RoomService/CreateRoom`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${process.env.LIVEKIT_API_KEY}`,
+ },
+ body: JSON.stringify({
+ room: roomName,
+ max_participants: maxParticipants,
+ empty_timeout: 300,
+ metadata: JSON.stringify({
+ created_at: new Date().toISOString(),
+ }),
+ }),
+ }
+ );
+
+ if (!response.ok) {
+ throw new Error(`Failed to create room: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.error('Error creating LiveKit room:', error);
+ throw error;
+ }
+}
+
+/**
+ * Delete room from LiveKit server
+ */
+export async function deleteLiveKitRoom(roomName) {
+ try {
+ const response = await fetch(
+ `${process.env.VITE_LIVEKIT_API_URL}/twirp/livekit.RoomService/DeleteRoom`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${process.env.LIVEKIT_API_KEY}`,
+ },
+ body: JSON.stringify({ room: roomName }),
+ }
+ );
+
+ if (!response.ok) {
+ throw new Error(`Failed to delete room: ${response.statusText}`);
+ }
+
+ return true;
+ } catch (error) {
+ console.error('Error deleting LiveKit room:', error);
+ throw error;
+ }
+}
+
+export default {
+ generateLiveKitToken,
+ getLiveKitUrl,
+ createLiveKitRoom,
+ deleteLiveKitRoom,
+};
diff --git a/astro-site/src/react-app/services/livekitService.js b/astro-site/src/react-app/services/livekitService.js
new file mode 100644
index 0000000..81b007e
--- /dev/null
+++ b/astro-site/src/react-app/services/livekitService.js
@@ -0,0 +1,181 @@
+/**
+ * LiveKit Service
+ * Handles voice/video calls integration
+ */
+
+import { LiveClient, Room } from 'livekit-client';
+
+class LiveKitService {
+ constructor() {
+ this.room = null;
+ this.participants = new Map();
+ this.isConnected = false;
+ }
+
+ /**
+ * Connect to LiveKit room
+ */
+ async connectToRoom(url, token) {
+ try {
+ this.room = new Room({
+ audio: true,
+ video: {
+ resolution: {
+ width: 1280,
+ height: 720,
+ },
+ },
+ adaptiveStream: true,
+ dynacast: true,
+ });
+
+ // Subscribe to events
+ this.room.on('participantConnected', (participant) => {
+ console.log('Participant connected:', participant.identity);
+ this.participants.set(participant.sid, participant);
+ });
+
+ this.room.on('participantDisconnected', (participant) => {
+ console.log('Participant disconnected:', participant.identity);
+ this.participants.delete(participant.sid);
+ });
+
+ this.room.on('trackSubscribed', (track, publication, participant) => {
+ console.log('Track subscribed:', track.kind);
+ });
+
+ await this.room.connect(url, token);
+ this.isConnected = true;
+
+ return this.room;
+ } catch (error) {
+ console.error('Error connecting to LiveKit room:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * Enable/disable microphone
+ */
+ async setMicEnabled(enabled) {
+ if (!this.room) return;
+
+ try {
+ if (enabled) {
+ await this.room.localParticipant.setMicrophoneEnabled(true);
+ } else {
+ await this.room.localParticipant.setMicrophoneEnabled(false);
+ }
+ } catch (error) {
+ console.error('Error setting microphone:', error);
+ }
+ }
+
+ /**
+ * Enable/disable camera
+ */
+ async setCameraEnabled(enabled) {
+ if (!this.room) return;
+
+ try {
+ if (enabled) {
+ await this.room.localParticipant.setCameraEnabled(true);
+ } else {
+ await this.room.localParticipant.setCameraEnabled(false);
+ }
+ } catch (error) {
+ console.error('Error setting camera:', error);
+ }
+ }
+
+ /**
+ * Start screen share
+ */
+ async startScreenShare() {
+ if (!this.room) return;
+
+ try {
+ const stream = await navigator.mediaDevices.getDisplayMedia({
+ video: true,
+ audio: false,
+ });
+
+ await this.room.localParticipant.setScreenShareEnabled(true);
+
+ stream.getTracks().forEach((track) => {
+ track.onended = () => {
+ this.room.localParticipant.setScreenShareEnabled(false);
+ };
+ });
+
+ return stream;
+ } catch (error) {
+ console.error('Error starting screen share:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * Stop screen share
+ */
+ async stopScreenShare() {
+ if (!this.room) return;
+
+ try {
+ await this.room.localParticipant.setScreenShareEnabled(false);
+ } catch (error) {
+ console.error('Error stopping screen share:', error);
+ }
+ }
+
+ /**
+ * Get participants
+ */
+ getParticipants() {
+ return Array.from(this.participants.values());
+ }
+
+ /**
+ * Disconnect from room
+ */
+ async disconnect() {
+ if (this.room) {
+ await this.room.disconnect();
+ this.isConnected = false;
+ this.participants.clear();
+ }
+ }
+
+ /**
+ * Send data message (for chat in calls)
+ */
+ sendDataMessage(topic, data) {
+ if (!this.room) return;
+
+ this.room.localParticipant.publishData(
+ new TextEncoder().encode(JSON.stringify(data)),
+ {
+ destinationIdentities: undefined,
+ topic,
+ }
+ );
+ }
+
+ /**
+ * Listen for data messages
+ */
+ onDataMessage(callback) {
+ if (!this.room) return;
+
+ this.room.on('dataReceived', (payload, participant) => {
+ try {
+ const data = JSON.parse(new TextDecoder().decode(payload));
+ callback(data, participant);
+ } catch (error) {
+ console.error('Error parsing data message:', error);
+ }
+ });
+ }
+}
+
+export default new LiveKitService();
diff --git a/astro-site/src/react-app/services/socketClient.js b/astro-site/src/react-app/services/socketClient.js
new file mode 100644
index 0000000..4c02e7c
--- /dev/null
+++ b/astro-site/src/react-app/services/socketClient.js
@@ -0,0 +1,229 @@
+/**
+ * Socket.IO Client Service
+ * Handles real-time communication with backend
+ */
+
+import io from 'socket.io-client';
+
+class SocketIOClient {
+ constructor() {
+ this.socket = null;
+ this.isConnected = false;
+ this.listeners = new Map();
+ }
+
+ /**
+ * Initialize Socket.IO connection
+ */
+ connect(url, token) {
+ return new Promise((resolve, reject) => {
+ try {
+ this.socket = io(url || 'http://localhost:3000', {
+ auth: {
+ token: token,
+ },
+ reconnection: true,
+ reconnectionDelay: 1000,
+ reconnectionDelayMax: 5000,
+ reconnectionAttempts: 5,
+ });
+
+ this.socket.on('connect', () => {
+ console.log('Socket.IO connected:', this.socket.id);
+ this.isConnected = true;
+ this.emit('connected', { socketId: this.socket.id });
+ resolve(this.socket);
+ });
+
+ this.socket.on('disconnect', (reason) => {
+ console.log('Socket.IO disconnected:', reason);
+ this.isConnected = false;
+ this.emit('disconnected', { reason });
+ });
+
+ this.socket.on('connect_error', (error) => {
+ console.error('Socket.IO connection error:', error);
+ reject(error);
+ });
+ } catch (error) {
+ reject(error);
+ }
+ });
+ }
+
+ /**
+ * Emit event
+ */
+ emit(event, data) {
+ if (this.socket) {
+ this.socket.emit(event, data);
+ }
+ }
+
+ /**
+ * Listen for events
+ */
+ on(event, callback) {
+ if (this.socket) {
+ this.socket.on(event, callback);
+ }
+
+ // Store listener for cleanup
+ if (!this.listeners.has(event)) {
+ this.listeners.set(event, []);
+ }
+ this.listeners.get(event).push(callback);
+ }
+
+ /**
+ * Remove all listeners for an event
+ */
+ off(event) {
+ if (this.socket) {
+ this.socket.off(event);
+ }
+ this.listeners.delete(event);
+ }
+
+ /**
+ * Disconnect socket
+ */
+ disconnect() {
+ if (this.socket) {
+ this.socket.disconnect();
+ }
+ this.isConnected = false;
+ }
+
+ /**
+ * Join channel room
+ */
+ joinChannel(channelId) {
+ if (this.socket) {
+ this.socket.emit('join:channel', { channelId });
+ }
+ }
+
+ /**
+ * Leave channel room
+ */
+ leaveChannel(channelId) {
+ if (this.socket) {
+ this.socket.emit('leave:channel', { channelId });
+ }
+ }
+
+ /**
+ * Send message in real-time
+ */
+ sendMessage(channelId, content, attachments = []) {
+ if (this.socket) {
+ this.socket.emit('message:send', {
+ channelId,
+ content,
+ attachments,
+ timestamp: new Date().toISOString(),
+ });
+ }
+ }
+
+ /**
+ * Edit message in real-time
+ */
+ editMessage(messageId, content) {
+ if (this.socket) {
+ this.socket.emit('message:edit', {
+ messageId,
+ content,
+ timestamp: new Date().toISOString(),
+ });
+ }
+ }
+
+ /**
+ * Delete message in real-time
+ */
+ deleteMessage(messageId) {
+ if (this.socket) {
+ this.socket.emit('message:delete', { messageId });
+ }
+ }
+
+ /**
+ * Add reaction to message
+ */
+ addReaction(messageId, emoji) {
+ if (this.socket) {
+ this.socket.emit('message:react', { messageId, emoji });
+ }
+ }
+
+ /**
+ * Set typing status
+ */
+ setTyping(channelId, isTyping) {
+ if (this.socket) {
+ this.socket.emit('user:typing', {
+ channelId,
+ isTyping,
+ });
+ }
+ }
+
+ /**
+ * Update user status
+ */
+ setUserStatus(status) {
+ if (this.socket) {
+ this.socket.emit('user:status', { status });
+ }
+ }
+
+ /**
+ * Start voice call
+ */
+ startVoiceCall(partnerId) {
+ if (this.socket) {
+ this.socket.emit('call:voice', { partnerId });
+ }
+ }
+
+ /**
+ * Start video call
+ */
+ startVideoCall(partnerId) {
+ if (this.socket) {
+ this.socket.emit('call:video', { partnerId });
+ }
+ }
+
+ /**
+ * Answer call
+ */
+ answerCall(callId) {
+ if (this.socket) {
+ this.socket.emit('call:answer', { callId });
+ }
+ }
+
+ /**
+ * Reject call
+ */
+ rejectCall(callId) {
+ if (this.socket) {
+ this.socket.emit('call:reject', { callId });
+ }
+ }
+
+ /**
+ * End call
+ */
+ endCall(callId) {
+ if (this.socket) {
+ this.socket.emit('call:end', { callId });
+ }
+ }
+}
+
+// Export singleton instance
+export default new SocketIOClient();
diff --git a/astro-site/src/react-app/services/socketIOService.js b/astro-site/src/react-app/services/socketIOService.js
new file mode 100644
index 0000000..92b3f45
--- /dev/null
+++ b/astro-site/src/react-app/services/socketIOService.js
@@ -0,0 +1,282 @@
+/**
+ * Socket.IO Client Service
+ * Manages real-time communication with backend
+ */
+
+import { io } from 'socket.io-client';
+
+class SocketIOService {
+ constructor() {
+ this.socket = null;
+ this.listeners = new Map();
+ this.isConnected = false;
+ }
+
+ /**
+ * Connect to Socket.IO server
+ */
+ connect(token, serverUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000') {
+ return new Promise((resolve, reject) => {
+ try {
+ this.socket = io(serverUrl, {
+ auth: {
+ token: token,
+ },
+ reconnection: true,
+ reconnectionDelay: 1000,
+ reconnectionDelayMax: 5000,
+ reconnectionAttempts: 5,
+ });
+
+ // Connection events
+ this.socket.on('connect', () => {
+ console.log('✓ Connected to Socket.IO server');
+ this.isConnected = true;
+ this.emit('connected');
+ resolve(this.socket);
+ });
+
+ this.socket.on('connect_error', (error) => {
+ console.error('Socket.IO connection error:', error);
+ this.emit('connection_error', error);
+ reject(error);
+ });
+
+ this.socket.on('disconnect', (reason) => {
+ console.log('Disconnected from Socket.IO:', reason);
+ this.isConnected = false;
+ this.emit('disconnected', reason);
+ });
+
+ this.socket.on('error', (error) => {
+ console.error('Socket.IO error:', error);
+ this.emit('error', error);
+ });
+ } catch (error) {
+ reject(error);
+ }
+ });
+ }
+
+ /**
+ * Disconnect from Socket.IO server
+ */
+ disconnect() {
+ if (this.socket) {
+ this.socket.disconnect();
+ this.isConnected = false;
+ this.socket = null;
+ }
+ }
+
+ /**
+ * Subscribe to event
+ */
+ on(event, callback) {
+ if (!this.listeners.has(event)) {
+ this.listeners.set(event, []);
+ }
+ this.listeners.get(event).push(callback);
+
+ if (this.socket) {
+ this.socket.on(event, callback);
+ }
+ }
+
+ /**
+ * Emit event with callback
+ */
+ emit(event, data, callback) {
+ if (this.socket && this.isConnected) {
+ this.socket.emit(event, data, callback);
+ } else {
+ console.warn(`Socket not connected, cannot emit ${event}`);
+ }
+ }
+
+ /**
+ * Unsubscribe from event
+ */
+ off(event, callback) {
+ if (this.socket) {
+ this.socket.off(event, callback);
+ }
+ if (this.listeners.has(event)) {
+ const callbacks = this.listeners.get(event);
+ const index = callbacks.indexOf(callback);
+ if (index > -1) {
+ callbacks.splice(index, 1);
+ }
+ }
+ }
+
+ /**
+ * Messaging events
+ */
+
+ joinConversation(conversationId) {
+ this.emit('join_conversation', { conversationId });
+ }
+
+ leaveConversation(conversationId) {
+ this.emit('leave_conversation', { conversationId });
+ }
+
+ sendMessage(conversationId, message) {
+ this.emit('message:send', { conversationId, message });
+ }
+
+ editMessage(conversationId, messageId, content) {
+ this.emit('message:edit', { conversationId, messageId, content });
+ }
+
+ deleteMessage(conversationId, messageId) {
+ this.emit('message:delete', { conversationId, messageId });
+ }
+
+ reactToMessage(conversationId, messageId, emoji) {
+ this.emit('message:react', { conversationId, messageId, emoji });
+ }
+
+ /**
+ * Typing events
+ */
+
+ typingStart(conversationId) {
+ this.emit('typing_start', { conversationId });
+ }
+
+ typingStop(conversationId) {
+ this.emit('typing_stop', { conversationId });
+ }
+
+ /**
+ * Voice channel events
+ */
+
+ joinVoiceChannel(channelId) {
+ this.emit('voice:join', { channelId });
+ }
+
+ leaveVoiceChannel(channelId) {
+ this.emit('voice:leave', { channelId });
+ }
+
+ sendVoiceSignal(data) {
+ this.emit('voice:signal', data);
+ }
+
+ /**
+ * Call signaling events
+ */
+
+ sendCallOffer(callId, targetUserId, offer) {
+ this.emit('call:offer', { callId, targetUserId, offer });
+ }
+
+ sendCallAnswer(callId, targetUserId, answer) {
+ this.emit('call:answer', { callId, targetUserId, answer });
+ }
+
+ sendICECandidate(callId, targetUserId, candidate) {
+ this.emit('call:ice-candidate', { callId, targetUserId, candidate });
+ }
+
+ /**
+ * Listen for incoming messages
+ */
+ onNewMessage(callback) {
+ this.on('new_message', callback);
+ }
+
+ /**
+ * Listen for message updates
+ */
+ onMessageEdited(callback) {
+ this.on('message:edited', callback);
+ }
+
+ onMessageDeleted(callback) {
+ this.on('message:deleted', callback);
+ }
+
+ onMessageReacted(callback) {
+ this.on('message:reacted', callback);
+ }
+
+ /**
+ * Listen for typing indicators
+ */
+ onUsertyping(callback) {
+ this.on('user_typing', callback);
+ }
+
+ onUserStoppedTyping(callback) {
+ this.on('user_stopped_typing', callback);
+ }
+
+ /**
+ * Listen for voice events
+ */
+ onVoiceUserJoined(callback) {
+ this.on('voice:user_joined', callback);
+ }
+
+ onVoiceUserLeft(callback) {
+ this.on('voice:user_left', callback);
+ }
+
+ onVoiceSignal(callback) {
+ this.on('voice:signal', callback);
+ }
+
+ /**
+ * Listen for status changes
+ */
+ onUserStatusChanged(callback) {
+ this.on('user_status_changed', callback);
+ }
+
+ /**
+ * Listen for call events
+ */
+ onCallOffer(callback) {
+ this.on('call:offer', callback);
+ }
+
+ onCallAnswer(callback) {
+ this.on('call:answer', callback);
+ }
+
+ onICECandidate(callback) {
+ this.on('call:ice-candidate', callback);
+ }
+
+ onCallEnded(callback) {
+ this.on('call:ended', callback);
+ }
+
+ onParticipantJoined(callback) {
+ this.on('call:participant-joined', callback);
+ }
+
+ onParticipantLeft(callback) {
+ this.on('call:participant-left', callback);
+ }
+
+ /**
+ * Get connection status
+ */
+ getIsConnected() {
+ return this.isConnected;
+ }
+
+ /**
+ * Get socket instance (if needed for advanced usage)
+ */
+ getSocket() {
+ return this.socket;
+ }
+}
+
+export default new SocketIOService();
diff --git a/astro-site/src/react-app/services/socketService.js b/astro-site/src/react-app/services/socketService.js
new file mode 100644
index 0000000..cf06fc4
--- /dev/null
+++ b/astro-site/src/react-app/services/socketService.js
@@ -0,0 +1,237 @@
+/**
+ * Socket.IO Service
+ * Handles real-time communication with backend
+ */
+
+import io from 'socket.io-client';
+
+class SocketService {
+ constructor() {
+ this.socket = null;
+ this.isConnected = false;
+ this.eventListeners = new Map();
+ }
+
+ /**
+ * Connect to Socket.IO server
+ */
+ connect(url = 'http://localhost:3000', options = {}) {
+ if (this.socket?.connected) return;
+
+ this.socket = io(url, {
+ reconnection: true,
+ reconnectionDelay: 1000,
+ reconnectionDelayMax: 5000,
+ reconnectionAttempts: 5,
+ transports: ['websocket', 'polling'],
+ ...options,
+ });
+
+ // Connection events
+ this.socket.on('connect', () => {
+ console.log('✅ Socket connected:', this.socket.id);
+ this.isConnected = true;
+ this.emit('connected');
+ });
+
+ this.socket.on('disconnect', () => {
+ console.log('❌ Socket disconnected');
+ this.isConnected = false;
+ this.emit('disconnected');
+ });
+
+ this.socket.on('connect_error', (error) => {
+ console.error('🔴 Connection error:', error);
+ this.emit('error', error);
+ });
+
+ return this.socket;
+ }
+
+ /**
+ * Disconnect from Socket.IO server
+ */
+ disconnect() {
+ if (this.socket) {
+ this.socket.disconnect();
+ this.isConnected = false;
+ }
+ }
+
+ /**
+ * Join a channel room
+ */
+ joinChannel(channelId) {
+ this.emit('join-channel', { channelId });
+ }
+
+ /**
+ * Leave a channel room
+ */
+ leaveChannel(channelId) {
+ this.emit('leave-channel', { channelId });
+ }
+
+ /**
+ * Send a message
+ */
+ sendMessage(channelId, message) {
+ this.emit('message-send', {
+ channelId,
+ content: message.content,
+ attachments: message.attachments,
+ replyToId: message.replyToId,
+ });
+ }
+
+ /**
+ * Edit a message
+ */
+ editMessage(messageId, content) {
+ this.emit('message-edit', { messageId, content });
+ }
+
+ /**
+ * Delete a message
+ */
+ deleteMessage(messageId) {
+ this.emit('message-delete', { messageId });
+ }
+
+ /**
+ * Add reaction to message
+ */
+ addReaction(messageId, emoji) {
+ this.emit('reaction-add', { messageId, emoji });
+ }
+
+ /**
+ * Remove reaction from message
+ */
+ removeReaction(messageId, emoji) {
+ this.emit('reaction-remove', { messageId, emoji });
+ }
+
+ /**
+ * Send typing indicator
+ */
+ sendTypingIndicator(channelId, username) {
+ this.emit('user-typing', { channelId, username });
+ }
+
+ /**
+ * Send stop typing indicator
+ */
+ sendStopTypingIndicator(channelId) {
+ this.emit('user-stop-typing', { channelId });
+ }
+
+ /**
+ * Update user status
+ */
+ updateStatus(status) {
+ this.emit('user-status', { status });
+ }
+
+ /**
+ * Listen to incoming events
+ */
+ on(event, callback) {
+ if (!this.eventListeners.has(event)) {
+ this.eventListeners.set(event, []);
+ }
+ this.eventListeners.get(event).push(callback);
+
+ // Also register with Socket.IO
+ if (this.socket) {
+ this.socket.on(event, callback);
+ }
+ }
+
+ /**
+ * Remove event listener
+ */
+ off(event, callback) {
+ if (this.eventListeners.has(event)) {
+ const callbacks = this.eventListeners.get(event);
+ const index = callbacks.indexOf(callback);
+ if (index > -1) {
+ callbacks.splice(index, 1);
+ }
+ }
+
+ if (this.socket) {
+ this.socket.off(event, callback);
+ }
+ }
+
+ /**
+ * Emit event to server
+ */
+ emit(event, data) {
+ if (this.socket) {
+ this.socket.emit(event, data);
+ } else {
+ console.warn('Socket not connected, cannot emit:', event);
+ }
+ }
+
+ /**
+ * Listen to new messages
+ */
+ onNewMessage(callback) {
+ this.on('message-received', callback);
+ }
+
+ /**
+ * Listen to message edits
+ */
+ onMessageEdited(callback) {
+ this.on('message-edited', callback);
+ }
+
+ /**
+ * Listen to message deletes
+ */
+ onMessageDeleted(callback) {
+ this.on('message-deleted', callback);
+ }
+
+ /**
+ * Listen to reactions
+ */
+ onReaction(callback) {
+ this.on('reaction-received', callback);
+ }
+
+ /**
+ * Listen to typing indicators
+ */
+ onUserTyping(callback) {
+ this.on('user-typing-received', callback);
+ }
+
+ /**
+ * Listen to user status changes
+ */
+ onUserStatusChange(callback) {
+ this.on('user-status-change', callback);
+ }
+
+ /**
+ * Listen to member list updates
+ */
+ onMembersUpdate(callback) {
+ this.on('members-updated', callback);
+ }
+
+ /**
+ * Listen to channel updates
+ */
+ onChannelsUpdate(callback) {
+ this.on('channels-updated', callback);
+ }
+}
+
+// Export singleton
+export default new SocketService();
diff --git a/astro-site/src/react-app/services/supabaseService.js b/astro-site/src/react-app/services/supabaseService.js
new file mode 100644
index 0000000..7f2c341
--- /dev/null
+++ b/astro-site/src/react-app/services/supabaseService.js
@@ -0,0 +1,298 @@
+/**
+ * Supabase Service
+ * Handles persistent data storage and retrieval
+ */
+
+import { createClient } from '@supabase/supabase-js';
+
+class SupabaseService {
+ constructor() {
+ this.client = null;
+ }
+
+ /**
+ * Initialize Supabase client
+ */
+ init(supabaseUrl, supabaseKey) {
+ if (!supabaseUrl || !supabaseKey) {
+ console.warn('Supabase credentials not provided');
+ return null;
+ }
+
+ this.client = createClient(supabaseUrl, supabaseKey);
+ return this.client;
+ }
+
+ /**
+ * Get all messages for a channel
+ */
+ async getChannelMessages(channelId, limit = 50, offset = 0) {
+ if (!this.client) return [];
+
+ const { data, error } = await this.client
+ .from('messages')
+ .select('*')
+ .eq('channel_id', channelId)
+ .order('created_at', { ascending: false })
+ .range(offset, offset + limit - 1);
+
+ if (error) {
+ console.error('Error fetching messages:', error);
+ return [];
+ }
+
+ return data?.reverse() || [];
+ }
+
+ /**
+ * Save a message
+ */
+ async saveMessage(channelId, userId, content, attachments = []) {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('messages')
+ .insert([
+ {
+ channel_id: channelId,
+ user_id: userId,
+ content,
+ metadata: { attachments },
+ created_at: new Date().toISOString(),
+ },
+ ])
+ .select();
+
+ if (error) {
+ console.error('Error saving message:', error);
+ return null;
+ }
+
+ return data?.[0] || null;
+ }
+
+ /**
+ * Update a message
+ */
+ async updateMessage(messageId, content) {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('messages')
+ .update({
+ content,
+ edited_at: new Date().toISOString(),
+ })
+ .eq('id', messageId)
+ .select();
+
+ if (error) {
+ console.error('Error updating message:', error);
+ return null;
+ }
+
+ return data?.[0] || null;
+ }
+
+ /**
+ * Delete a message
+ */
+ async deleteMessage(messageId) {
+ if (!this.client) return false;
+
+ const { error } = await this.client
+ .from('messages')
+ .delete()
+ .eq('id', messageId);
+
+ if (error) {
+ console.error('Error deleting message:', error);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get all channels
+ */
+ async getChannels() {
+ if (!this.client) return [];
+
+ const { data, error } = await this.client
+ .from('channels')
+ .select('*')
+ .order('created_at', { ascending: true });
+
+ if (error) {
+ console.error('Error fetching channels:', error);
+ return [];
+ }
+
+ return data || [];
+ }
+
+ /**
+ * Create a channel
+ */
+ async createChannel(name, description, categoryId, type = 'text') {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('channels')
+ .insert([
+ {
+ name,
+ description,
+ category_id: categoryId,
+ type,
+ created_at: new Date().toISOString(),
+ },
+ ])
+ .select();
+
+ if (error) {
+ console.error('Error creating channel:', error);
+ return null;
+ }
+
+ return data?.[0] || null;
+ }
+
+ /**
+ * Get server members
+ */
+ async getServerMembers(serverId) {
+ if (!this.client) return [];
+
+ const { data, error } = await this.client
+ .from('members')
+ .select('*, users(*)')
+ .eq('server_id', serverId);
+
+ if (error) {
+ console.error('Error fetching members:', error);
+ return [];
+ }
+
+ return data || [];
+ }
+
+ /**
+ * Add reaction to message
+ */
+ async addReaction(messageId, userId, emoji) {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('reactions')
+ .insert([
+ {
+ message_id: messageId,
+ user_id: userId,
+ emoji,
+ created_at: new Date().toISOString(),
+ },
+ ])
+ .select();
+
+ if (error) {
+ console.error('Error adding reaction:', error);
+ return null;
+ }
+
+ return data?.[0] || null;
+ }
+
+ /**
+ * Remove reaction from message
+ */
+ async removeReaction(messageId, userId, emoji) {
+ if (!this.client) return false;
+
+ const { error } = await this.client
+ .from('reactions')
+ .delete()
+ .eq('message_id', messageId)
+ .eq('user_id', userId)
+ .eq('emoji', emoji);
+
+ if (error) {
+ console.error('Error removing reaction:', error);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get user by ID
+ */
+ async getUser(userId) {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('users')
+ .select('*')
+ .eq('id', userId)
+ .single();
+
+ if (error) {
+ console.error('Error fetching user:', error);
+ return null;
+ }
+
+ return data;
+ }
+
+ /**
+ * Update user status
+ */
+ async updateUserStatus(userId, status) {
+ if (!this.client) return null;
+
+ const { data, error } = await this.client
+ .from('users')
+ .update({
+ status,
+ updated_at: new Date().toISOString(),
+ })
+ .eq('id', userId)
+ .select();
+
+ if (error) {
+ console.error('Error updating user status:', error);
+ return null;
+ }
+
+ return data?.[0] || null;
+ }
+
+ /**
+ * Search messages
+ */
+ async searchMessages(query, channelId = null) {
+ if (!this.client) return [];
+
+ let queryBuilder = this.client
+ .from('messages')
+ .select('*')
+ .ilike('content', `%${query}%`);
+
+ if (channelId) {
+ queryBuilder = queryBuilder.eq('channel_id', channelId);
+ }
+
+ const { data, error } = await queryBuilder;
+
+ if (error) {
+ console.error('Error searching messages:', error);
+ return [];
+ }
+
+ return data || [];
+ }
+}
+
+// Export singleton
+export default new SupabaseService();
diff --git a/astro-site/src/react-app/stores/messageStoreIntegrated.js b/astro-site/src/react-app/stores/messageStoreIntegrated.js
new file mode 100644
index 0000000..71fba7c
--- /dev/null
+++ b/astro-site/src/react-app/stores/messageStoreIntegrated.js
@@ -0,0 +1,370 @@
+/**
+ * Enhanced Message Store
+ * Integrates with Socket.IO and REST API for real-time messaging
+ */
+
+import { create } from 'zustand';
+import socketClient from '../services/socketClient';
+
+// API base URL
+const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
+
+export const useMessageStore = create((set, get) => ({
+ // State
+ messages: {},
+ channelMessages: {}, // channelId -> [messages]
+ currentChannelId: null,
+ loading: false,
+ error: null,
+ typingUsers: {},
+ hasMore: {},
+ isLoadingMore: {},
+
+ // Initialize Socket.IO listeners
+ initializeSocketListeners: () => {
+ // Receive message
+ socketClient.on('message:receive', (message) => {
+ const { addMessage } = get();
+ addMessage(message);
+ });
+
+ // Message edited
+ socketClient.on('message:edited', (data) => {
+ const { editMessage } = get();
+ editMessage(data.messageId, data.content);
+ });
+
+ // Message deleted
+ socketClient.on('message:deleted', (data) => {
+ const { deleteMessage } = get();
+ deleteMessage(data.messageId);
+ });
+
+ // User typing
+ socketClient.on('user:typing', (data) => {
+ const { addTypingUser } = get();
+ addTypingUser(data.channelId, data.username);
+ });
+
+ // User stopped typing
+ socketClient.on('user:stop-typing', (data) => {
+ const { removeTypingUser } = get();
+ removeTypingUser(data.channelId, data.username);
+ });
+
+ // Reaction added
+ socketClient.on('message:reaction', (data) => {
+ const { addReaction } = get();
+ addReaction(data.messageId, data.emoji, data.userId);
+ });
+ },
+
+ // Fetch messages for channel
+ fetchMessages: async (channelId, limit = 50, offset = 0) => {
+ set({ loading: true, error: null });
+ try {
+ const token = localStorage.getItem('token');
+ const response = await fetch(
+ `${API_URL}/api/message/channel/${channelId}?limit=${limit}&offset=${offset}`,
+ {
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+
+ if (!response.ok) throw new Error('Failed to fetch messages');
+
+ const data = await response.json();
+
+ set((state) => ({
+ channelMessages: {
+ ...state.channelMessages,
+ [channelId]: data.messages,
+ },
+ hasMore: {
+ ...state.hasMore,
+ [channelId]: data.messages.length === limit,
+ },
+ loading: false,
+ }));
+
+ return data.messages;
+ } catch (error) {
+ console.error('Error fetching messages:', error);
+ set({ error: error.message, loading: false });
+ return [];
+ }
+ },
+
+ // Load more messages (for infinite scroll)
+ loadMoreMessages: async (channelId) => {
+ const { channelMessages } = get();
+ const currentMessages = channelMessages[channelId] || [];
+ const offset = currentMessages.length;
+
+ set((state) => ({
+ isLoadingMore: {
+ ...state.isLoadingMore,
+ [channelId]: true,
+ },
+ }));
+
+ try {
+ const token = localStorage.getItem('token');
+ const response = await fetch(
+ `${API_URL}/api/message/channel/${channelId}?limit=50&offset=${offset}`,
+ {
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+
+ if (!response.ok) throw new Error('Failed to load more messages');
+
+ const data = await response.json();
+
+ set((state) => ({
+ channelMessages: {
+ ...state.channelMessages,
+ [channelId]: [...(state.channelMessages[channelId] || []), ...data.messages],
+ },
+ hasMore: {
+ ...state.hasMore,
+ [channelId]: data.messages.length === 50,
+ },
+ isLoadingMore: {
+ ...state.isLoadingMore,
+ [channelId]: false,
+ },
+ }));
+
+ return data.messages;
+ } catch (error) {
+ console.error('Error loading more messages:', error);
+ set((state) => ({
+ isLoadingMore: {
+ ...state.isLoadingMore,
+ [channelId]: false,
+ },
+ error: error.message,
+ }));
+ return [];
+ }
+ },
+
+ // Send message
+ sendMessage: async (channelId, content, attachments = []) => {
+ try {
+ const token = localStorage.getItem('token');
+
+ // Optimistic update
+ const tempMessage = {
+ id: `temp-${Date.now()}`,
+ content,
+ channelId,
+ senderId: localStorage.getItem('userId'),
+ createdAt: new Date().toISOString(),
+ _sending: true,
+ };
+
+ set((state) => ({
+ channelMessages: {
+ ...state.channelMessages,
+ [channelId]: [...(state.channelMessages[channelId] || []), tempMessage],
+ },
+ }));
+
+ // Send via API + Socket.IO
+ const response = await fetch(`${API_URL}/api/message`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ content,
+ channelId,
+ attachments,
+ }),
+ });
+
+ if (!response.ok) throw new Error('Failed to send message');
+
+ const data = await response.json();
+
+ // Emit via Socket.IO
+ socketClient.sendMessage(channelId, content, attachments);
+
+ // Remove temp message and add real one
+ set((state) => ({
+ channelMessages: {
+ ...state.channelMessages,
+ [channelId]: state.channelMessages[channelId]
+ .filter((m) => m.id !== tempMessage.id)
+ .concat(data.message),
+ },
+ }));
+
+ return data.message;
+ } catch (error) {
+ console.error('Error sending message:', error);
+ set({ error: error.message });
+ return null;
+ }
+ },
+
+ // Add message (from Socket.IO)
+ addMessage: (message) => {
+ set((state) => ({
+ channelMessages: {
+ ...state.channelMessages,
+ [message.channelId]: [
+ ...(state.channelMessages[message.channelId] || []),
+ message,
+ ],
+ },
+ }));
+ },
+
+ // Edit message
+ editMessage: async (messageId, content) => {
+ try {
+ const token = localStorage.getItem('token');
+
+ const response = await fetch(`${API_URL}/api/message/${messageId}`, {
+ method: 'PATCH',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ content }),
+ });
+
+ if (!response.ok) throw new Error('Failed to edit message');
+
+ const data = await response.json();
+
+ // Update local state
+ set((state) => {
+ const updated = { ...state.channelMessages };
+ const { currentChannelId } = state;
+
+ if (currentChannelId && updated[currentChannelId]) {
+ updated[currentChannelId] = updated[currentChannelId].map((msg) =>
+ msg.id === messageId ? { ...msg, ...data.message } : msg
+ );
+ }
+
+ return { channelMessages: updated };
+ });
+
+ // Emit via Socket.IO
+ socketClient.editMessage(messageId, content);
+
+ return data.message;
+ } catch (error) {
+ console.error('Error editing message:', error);
+ set({ error: error.message });
+ return null;
+ }
+ },
+
+ // Delete message
+ deleteMessage: async (messageId) => {
+ try {
+ const token = localStorage.getItem('token');
+
+ const response = await fetch(`${API_URL}/api/message/${messageId}`, {
+ method: 'DELETE',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
+
+ if (!response.ok) throw new Error('Failed to delete message');
+
+ // Update local state
+ set((state) => {
+ const updated = { ...state.channelMessages };
+ const { currentChannelId } = state;
+
+ if (currentChannelId && updated[currentChannelId]) {
+ updated[currentChannelId] = updated[currentChannelId].filter(
+ (msg) => msg.id !== messageId
+ );
+ }
+
+ return { channelMessages: updated };
+ });
+
+ // Emit via Socket.IO
+ socketClient.deleteMessage(messageId);
+
+ return true;
+ } catch (error) {
+ console.error('Error deleting message:', error);
+ set({ error: error.message });
+ return false;
+ }
+ },
+
+ // Add reaction
+ addReaction: async (messageId, emoji) => {
+ try {
+ const token = localStorage.getItem('token');
+
+ const response = await fetch(`${API_URL}/api/message/${messageId}/reactions`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ emoji }),
+ });
+
+ if (!response.ok) throw new Error('Failed to add reaction');
+
+ const data = await response.json();
+
+ // Emit via Socket.IO
+ socketClient.addReaction(messageId, emoji);
+
+ return data.reactions;
+ } catch (error) {
+ console.error('Error adding reaction:', error);
+ set({ error: error.message });
+ return [];
+ }
+ },
+
+ // Typing indicator
+ addTypingUser: (channelId, username) => {
+ set((state) => ({
+ typingUsers: {
+ ...state.typingUsers,
+ [channelId]: [...new Set([...(state.typingUsers[channelId] || []), username])],
+ },
+ }));
+ },
+
+ removeTypingUser: (channelId, username) => {
+ set((state) => ({
+ typingUsers: {
+ ...state.typingUsers,
+ [channelId]: (state.typingUsers[channelId] || []).filter((u) => u !== username),
+ },
+ }));
+ },
+
+ // Set current channel
+ setCurrentChannel: (channelId) => {
+ set({ currentChannelId: channelId });
+ },
+
+ // Clear error
+ clearError: () => set({ error: null }),
+}));
diff --git a/astro-site/src/react-app/utils/dateFormat.js b/astro-site/src/react-app/utils/dateFormat.js
new file mode 100644
index 0000000..f8250e7
--- /dev/null
+++ b/astro-site/src/react-app/utils/dateFormat.js
@@ -0,0 +1,93 @@
+/**
+ * Date Formatting Utilities
+ * Using date-fns for consistent, Discord-like date/time formatting
+ */
+
+import {
+ format,
+ formatDistanceToNow,
+ formatRelative,
+ isSameDay,
+ isSameYear,
+ parseISO,
+} from 'date-fns';
+
+/**
+ * Format message timestamp for display
+ * Shows time of day for recent messages, full date for older ones
+ */
+export const formatMessageTime = (timestamp, showFullDate = false) => {
+ const date = typeof timestamp === 'string' ? parseISO(timestamp) : timestamp;
+
+ if (showFullDate) {
+ return format(date, 'MMM d, yyyy h:mm a');
+ }
+
+ return format(date, 'h:mm a');
+};
+
+/**
+ * Format date divider between message groups
+ * Shows relative dates like "Today", "Yesterday", or full date
+ */
+export const formatDateDivider = (timestamp) => {
+ const date = typeof timestamp === 'string' ? parseISO(timestamp) : timestamp;
+ const now = new Date();
+
+ if (isSameDay(date, now)) {
+ return 'Today';
+ }
+
+ const yesterday = new Date(now);
+ yesterday.setDate(yesterday.getDate() - 1);
+ if (isSameDay(date, yesterday)) {
+ return 'Yesterday';
+ }
+
+ if (isSameYear(date, now)) {
+ return format(date, 'MMMM d');
+ }
+
+ return format(date, 'MMMM d, yyyy');
+};
+
+/**
+ * Format relative time for hovering over timestamps
+ * Shows "2 minutes ago" style formatting
+ */
+export const formatRelativeTime = (timestamp) => {
+ const date = typeof timestamp === 'string' ? parseISO(timestamp) : timestamp;
+
+ return formatDistanceToNow(date, { addSuffix: true });
+};
+
+/**
+ * Determine if date divider should be shown
+ * Shows divider if more than 5 minutes have passed or it's a new day
+ */
+export const shouldShowDateDivider = (currentMessage, previousMessage) => {
+ if (!previousMessage) return true; // Show divider for first message
+
+ const currentDate = parseISO(currentMessage.createdAt);
+ const previousDate = parseISO(previousMessage.createdAt);
+
+ // Show divider if different days
+ if (!isSameDay(currentDate, previousDate)) {
+ return true;
+ }
+
+ // Show divider if more than 5 minutes difference
+ const timeDiff = currentDate - previousDate;
+ return timeDiff > 5 * 60 * 1000;
+};
+
+/**
+ * Format edited badge with timestamp
+ * Shows "(edited)" or "(edited at 3:45 pm)"
+ */
+export const formatEditedBadge = (timestamp, showTime = false) => {
+ if (!showTime) return 'edited';
+
+ const date = typeof timestamp === 'string' ? parseISO(timestamp) : timestamp;
+ return `edited at ${format(date, 'h:mm a')}`;
+};
diff --git a/astro-site/src/stores/callStore.js b/astro-site/src/stores/callStore.js
new file mode 100644
index 0000000..152a955
--- /dev/null
+++ b/astro-site/src/stores/callStore.js
@@ -0,0 +1,198 @@
+/**
+ * Call Store
+ * Manages call state and room connections
+ */
+
+import { create } from 'zustand';
+
+export const useCallStore = create((set, get) => ({
+ activeCall: null,
+ incomingCalls: [],
+ callHistory: [],
+ isCallActive: false,
+
+ /**
+ * Start outgoing call
+ */
+ startCall: (participantId, participantName) =>
+ set((state) => {
+ const callId = `call-${Date.now()}`;
+ const call = {
+ id: callId,
+ type: 'outgoing',
+ participantId,
+ participantName,
+ startTime: new Date(),
+ status: 'ringing',
+ };
+
+ return {
+ activeCall: call,
+ isCallActive: true,
+ };
+ }),
+
+ /**
+ * Answer incoming call
+ */
+ answerCall: () =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ return {
+ activeCall: {
+ ...state.activeCall,
+ status: 'connected',
+ connectedTime: new Date(),
+ },
+ };
+ }),
+
+ /**
+ * Receive incoming call
+ */
+ receiveCall: (callData) =>
+ set((state) => {
+ const call = {
+ ...callData,
+ id: `incoming-${Date.now()}`,
+ type: 'incoming',
+ status: 'ringing',
+ receivedTime: new Date(),
+ };
+
+ return {
+ incomingCalls: [...state.incomingCalls, call],
+ };
+ }),
+
+ /**
+ * Reject incoming call
+ */
+ rejectCall: (callId) =>
+ set((state) => ({
+ incomingCalls: state.incomingCalls.filter((c) => c.id !== callId),
+ })),
+
+ /**
+ * End call
+ */
+ endCall: (reason = 'user-ended') =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ const callRecord = {
+ ...state.activeCall,
+ endTime: new Date(),
+ duration: Math.floor(
+ (new Date() - state.activeCall.startTime) / 1000
+ ),
+ endReason: reason,
+ };
+
+ return {
+ activeCall: null,
+ isCallActive: false,
+ callHistory: [callRecord, ...state.callHistory],
+ };
+ }),
+
+ /**
+ * Hold call
+ */
+ holdCall: () =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ return {
+ activeCall: {
+ ...state.activeCall,
+ status: 'held',
+ heldTime: new Date(),
+ },
+ };
+ }),
+
+ /**
+ * Resume call
+ */
+ resumeCall: () =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ return {
+ activeCall: {
+ ...state.activeCall,
+ status: 'connected',
+ },
+ };
+ }),
+
+ /**
+ * Mute call
+ */
+ setMuted: (muted) =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ return {
+ activeCall: {
+ ...state.activeCall,
+ isMuted: muted,
+ },
+ };
+ }),
+
+ /**
+ * Enable/disable video
+ */
+ setVideoEnabled: (enabled) =>
+ set((state) => {
+ if (!state.activeCall) return state;
+
+ return {
+ activeCall: {
+ ...state.activeCall,
+ videoEnabled: enabled,
+ },
+ };
+ }),
+
+ /**
+ * Add call to history (for completed calls)
+ */
+ addToHistory: (call) =>
+ set((state) => ({
+ callHistory: [call, ...state.callHistory],
+ })),
+
+ /**
+ * Clear call history
+ */
+ clearHistory: () =>
+ set(() => ({
+ callHistory: [],
+ })),
+
+ /**
+ * Get call duration
+ */
+ getCallDuration: () => {
+ const state = get();
+ if (!state.activeCall || state.activeCall.status !== 'connected') {
+ return 0;
+ }
+
+ return Math.floor((new Date() - state.activeCall.connectedTime) / 1000);
+ },
+
+ /**
+ * Clear all state
+ */
+ clear: () =>
+ set(() => ({
+ activeCall: null,
+ incomingCalls: [],
+ isCallActive: false,
+ })),
+}));
diff --git a/astro-site/src/stores/channelStore.js b/astro-site/src/stores/channelStore.js
new file mode 100644
index 0000000..9ea68df
--- /dev/null
+++ b/astro-site/src/stores/channelStore.js
@@ -0,0 +1,67 @@
+import { create } from 'zustand';
+import { channelService } from '../react-app/services/apiService.js';
+
+export const useChannelStore = create((set, get) => ({
+ channels: [],
+ currentChannelId: null,
+ isLoading: false,
+ error: null,
+
+ // Fetch channels for a server
+ fetchChannels: async (serverId) => {
+ if (!serverId) {
+ set({ channels: [], currentChannelId: null });
+ return;
+ }
+
+ set({ isLoading: true, error: null });
+ try {
+ const response = await channelService.list(serverId);
+ const channelsList = response.data || response;
+
+ set({
+ channels: Array.isArray(channelsList) ? channelsList : [],
+ isLoading: false
+ });
+
+ // Set first channel as current if not already set
+ if (channelsList.length > 0 && !get().currentChannelId) {
+ set({ currentChannelId: channelsList[0].id });
+ }
+ } catch (error) {
+ set({
+ error: error.message,
+ isLoading: false
+ });
+ }
+ },
+
+ setCurrentChannel: (channelId) =>
+ set(() => ({
+ currentChannelId: channelId,
+ })),
+
+ getCurrentChannel: () => {
+ const { channels, currentChannelId } = get();
+ return channels.find((c) => c.id === currentChannelId);
+ },
+
+ addChannel: (channel) =>
+ set((state) => ({
+ channels: [...state.channels, channel],
+ })),
+
+ removeChannel: (channelId) =>
+ set((state) => ({
+ channels: state.channels.filter((c) => c.id !== channelId),
+ currentChannelId:
+ state.currentChannelId === channelId ? null : state.currentChannelId,
+ })),
+
+ updateChannel: (channelId, updates) =>
+ set((state) => ({
+ channels: state.channels.map((c) =>
+ c.id === channelId ? { ...c, ...updates } : c
+ ),
+ })),
+}));
diff --git a/astro-site/src/stores/directMessageStore.js b/astro-site/src/stores/directMessageStore.js
new file mode 100644
index 0000000..f88cfbc
--- /dev/null
+++ b/astro-site/src/stores/directMessageStore.js
@@ -0,0 +1,82 @@
+import { create } from 'zustand';
+
+export const useDirectMessageStore = create((set, get) => ({
+ conversations: [
+ {
+ id: 'dm-1',
+ userId: 'user-2',
+ userName: 'Alex Rivera',
+ avatar: '🎮',
+ lastMessage: 'Hey! Did you see the updates?',
+ timestamp: Date.now() - 300000,
+ unread: 2,
+ messages: [
+ {
+ id: 'msg-1',
+ senderId: 'user-2',
+ text: 'Hey! Did you see the updates?',
+ timestamp: Date.now() - 300000,
+ },
+ {
+ id: 'msg-2',
+ senderId: 'user-1',
+ text: 'Just got them pushed through!',
+ timestamp: Date.now() - 240000,
+ },
+ ],
+ },
+ ],
+
+ currentConversationId: null,
+
+ setCurrentConversation: (conversationId) =>
+ set(() => ({
+ currentConversationId: conversationId,
+ })),
+
+ getCurrentConversation: () => {
+ const { conversations, currentConversationId } = get();
+ return conversations.find((c) => c.id === currentConversationId);
+ },
+
+ addMessage: (conversationId, message) =>
+ set((state) => ({
+ conversations: state.conversations.map((c) =>
+ c.id === conversationId
+ ? {
+ ...c,
+ messages: [...c.messages, message],
+ lastMessage: message.text,
+ timestamp: message.timestamp,
+ }
+ : c
+ ),
+ })),
+
+ createConversation: (userId, userName, avatar) =>
+ set((state) => {
+ const existingConv = state.conversations.find((c) => c.userId === userId);
+ if (existingConv) return state;
+
+ const newConv = {
+ id: `dm-${userId}`,
+ userId,
+ userName,
+ avatar,
+ lastMessage: '',
+ timestamp: Date.now(),
+ unread: 0,
+ messages: [],
+ };
+ return {
+ conversations: [...state.conversations, newConv],
+ };
+ }),
+
+ markread: (conversationId) =>
+ set((state) => ({
+ conversations: state.conversations.map((c) =>
+ c.id === conversationId ? { ...c, unread: 0 } : c
+ ),
+ })),
+}));
diff --git a/astro-site/src/stores/memberStore.js b/astro-site/src/stores/memberStore.js
new file mode 100644
index 0000000..5e65c1d
--- /dev/null
+++ b/astro-site/src/stores/memberStore.js
@@ -0,0 +1,95 @@
+import { create } from 'zustand';
+
+export const useMemberStore = create((set, get) => ({
+ members: [
+ {
+ id: 'anderson',
+ name: 'Anderson',
+ avatar: 'A',
+ status: 'online',
+ role: 'ADMIN',
+ division: 'Foundation',
+ avatarBg: 'from-red-600 to-red-800',
+ },
+ {
+ id: 'trevor',
+ name: 'Trevor',
+ avatar: 'T',
+ status: 'online',
+ role: 'MODERATOR',
+ division: 'Foundation',
+ avatarBg: 'from-red-600 to-red-800',
+ },
+ {
+ id: 'sarah',
+ name: 'Sarah',
+ avatar: 'S',
+ status: 'online',
+ role: 'MEMBER',
+ division: 'Labs',
+ avatarBg: 'from-orange-400 to-orange-700',
+ },
+ {
+ id: 'marcus',
+ name: 'Marcus',
+ avatar: 'M',
+ status: 'in-game',
+ role: 'MODERATOR',
+ division: 'Development',
+ avatarBg: 'from-blue-600 to-blue-900',
+ },
+ ],
+
+ currentUserId: 'anderson',
+
+ setCurrentUser: (userId) =>
+ set(() => ({
+ currentUserId: userId,
+ })),
+
+ getCurrentUser: () => {
+ const { members, currentUserId } = get();
+ return members.find((m) => m.id === currentUserId);
+ },
+
+ updateMemberRole: (memberId, newRole) =>
+ set((state) => ({
+ members: state.members.map((m) =>
+ m.id === memberId ? { ...m, role: newRole } : m
+ ),
+ })),
+
+ kickMember: (memberId) =>
+ set((state) => ({
+ members: state.members.filter((m) => m.id !== memberId),
+ })),
+
+ banMember: (memberId) =>
+ set((state) => ({
+ members: state.members.map((m) =>
+ m.id === memberId ? { ...m, status: 'banned' } : m
+ ),
+ })),
+
+ updateMemberStatus: (memberId, status, activity = '') =>
+ set((state) => ({
+ members: state.members.map((m) =>
+ m.id === memberId ? { ...m, status, activity } : m
+ ),
+ })),
+
+ addMember: (member) =>
+ set((state) => ({
+ members: [...state.members, member],
+ })),
+
+ getOnlineMembers: () => {
+ const { members } = get();
+ return members.filter((m) => m.status !== 'offline' && m.status !== 'banned');
+ },
+
+ getMembersByRole: (role) => {
+ const { members } = get();
+ return members.filter((m) => m.role === role);
+ },
+}));
diff --git a/astro-site/src/stores/messageStore.js b/astro-site/src/stores/messageStore.js
new file mode 100644
index 0000000..ffbfd9d
--- /dev/null
+++ b/astro-site/src/stores/messageStore.js
@@ -0,0 +1,291 @@
+import { create } from 'zustand';
+import { messageService } from '../react-app/services/apiService.js';
+import socketIOService from '../react-app/services/socketIOService';
+
+export const useMessageStore = create((set, get) => ({
+ messages: [],
+ typingUsers: [],
+ editingMessageId: null,
+ editingContent: '',
+ currentConversationId: null,
+ isLoading: false,
+ hasMore: true,
+ socketConnected: false,
+ pinnedMessages: [],
+ threadMessages: new Map(),
+
+ // Fetch messages from API
+ fetchMessages: async (channelId) => {
+ if (!channelId) {
+ set({ messages: [], currentConversationId: null });
+ return;
+ }
+
+ set({ isLoading: true, currentConversationId: channelId });
+ try {
+ const response = await messageService.list(channelId, 50, 0);
+ const messagesList = response.data || response;
+
+ set({
+ messages: Array.isArray(messagesList) ? messagesList : [],
+ isLoading: false,
+ hasMore: messagesList?.length === 50,
+ });
+ } catch (error) {
+ console.error('Failed to fetch messages:', error);
+ set({
+ isLoading: false
+ });
+ }
+ },
+
+ // Initialize Socket.IO listeners
+ initializeSocketListeners: () => {
+ // Message events
+ socketIOService.onNewMessage((message) => {
+ get().addMessage(message);
+ });
+
+ socketIOService.onMessageEdited((data) => {
+ get().updateMessage(data.messageId, {
+ content: data.content,
+ editedAt: new Date(),
+ });
+ });
+
+ socketIOService.onMessageDeleted((data) => {
+ get().deleteMessage(data.messageId);
+ });
+
+ socketIOService.onMessageReacted((data) => {
+ const { messageId, emoji, userId, action } = data;
+ if (action === 'add') {
+ get().addReaction(messageId, emoji, userId);
+ } else {
+ get().removeReaction(messageId, emoji, userId);
+ }
+ });
+
+ // Typing events
+ socketIOService.onUsertyping((data) => {
+ get().addTypingUser(data.userId, data.username);
+ });
+
+ socketIOService.onUserStoppedTyping((data) => {
+ get().removeTypingUser(data.userId);
+ });
+
+ set(() => ({ socketConnected: true }));
+ },
+
+ setMessages: (messages) =>
+ set(() => ({
+ messages,
+ })),
+
+ addMessage: (message) =>
+ set((state) => ({
+ messages: [...state.messages, message],
+ })),
+
+ prependMessages: (messages) =>
+ set((state) => ({
+ messages: [...messages, ...state.messages],
+ })),
+
+ updateMessage: (messageId, updates) =>
+ set((state) => ({
+ messages: state.messages.map((m) =>
+ m.id === messageId ? { ...m, ...updates } : m
+ ),
+ })),
+
+ deleteMessage: (messageId) =>
+ set((state) => ({
+ messages: state.messages.filter((m) => m.id !== messageId),
+ })),
+
+ startEditingMessage: (messageId, content) =>
+ set(() => ({
+ editingMessageId: messageId,
+ editingContent: content,
+ })),
+
+ cancelEditingMessage: () =>
+ set(() => ({
+ editingMessageId: null,
+ editingContent: '',
+ })),
+
+ saveEditedMessage: (messageId, newContent) => {
+ const state = get();
+ socketIOService.editMessage(state.currentConversationId, messageId, newContent);
+ set(() => ({
+ editingMessageId: null,
+ editingContent: '',
+ }));
+ },
+
+ addReaction: (messageId, emoji, userId) =>
+ set((state) => ({
+ messages: state.messages.map((m) => {
+ if (m.id === messageId) {
+ const reactions = m.reactions || [];
+ const existingReaction = reactions.find((r) => r.emoji === emoji);
+
+ if (existingReaction) {
+ if (!existingReaction.users.includes(userId)) {
+ existingReaction.users.push(userId);
+ }
+ } else {
+ reactions.push({ emoji, users: [userId] });
+ }
+
+ return { ...m, reactions };
+ }
+ return m;
+ }),
+ })),
+
+ removeReaction: (messageId, emoji, userId) =>
+ set((state) => ({
+ messages: state.messages.map((m) => {
+ if (m.id === messageId) {
+ const reactions = m.reactions || [];
+ return {
+ ...m,
+ reactions: reactions
+ .map((r) => ({
+ ...r,
+ users: r.emoji === emoji ? r.users.filter((u) => u !== userId) : r.users,
+ }))
+ .filter((r) => r.users.length > 0),
+ };
+ }
+ return m;
+ }),
+ })),
+
+ // Typing indicators
+ addTypingUser: (userId, username) =>
+ set((state) => {
+ const exists = state.typingUsers.some((u) => u.id === userId);
+ if (!exists) {
+ return {
+ typingUsers: [...state.typingUsers, { id: userId, username }],
+ };
+ }
+ return state;
+ }),
+
+ removeTypingUser: (userId) =>
+ set((state) => ({
+ typingUsers: state.typingUsers.filter((u) => u.id !== userId),
+ })),
+
+ clearTypingUsers: () =>
+ set(() => ({
+ typingUsers: [],
+ })),
+
+ // Conversation management
+ setCurrentConversationId: (conversationId) =>
+ set(() => ({ currentConversationId: conversationId })),
+
+ joinConversation: (conversationId) => {
+ socketIOService.joinConversation(conversationId);
+ get().setCurrentConversationId(conversationId);
+ },
+
+ leaveConversation: (conversationId) => {
+ socketIOService.leaveConversation(conversationId);
+ get().setCurrentConversationId(null);
+ get().setMessages([]);
+ },
+
+ // Send message
+ sendMessage: (content) => {
+ const state = get();
+ if (!state.currentConversationId || !content.trim()) return;
+
+ const message = {
+ id: `temp-${Date.now()}`,
+ content,
+ createdAt: new Date().toISOString(),
+ senderId: localStorage.getItem('userId'),
+ conversationId: state.currentConversationId,
+ _sending: true,
+ };
+
+ get().addMessage(message);
+ socketIOService.sendMessage(state.currentConversationId, message);
+ },
+
+ // Typing indicators
+ notifyTyping: () => {
+ const state = get();
+ if (state.currentConversationId) {
+ socketIOService.typingStart(state.currentConversationId);
+ }
+ },
+
+ notifyStoppedTyping: () => {
+ const state = get();
+ if (state.currentConversationId) {
+ socketIOService.typingStop(state.currentConversationId);
+ }
+ },
+
+ // Reactions
+ reactToMessage: (messageId, emoji) => {
+ const state = get();
+ if (state.currentConversationId) {
+ socketIOService.reactToMessage(state.currentConversationId, messageId, emoji);
+ }
+ },
+
+ // Pagination
+ setIsLoading: (isLoading) =>
+ set(() => ({ isLoading })),
+
+ setHasMore: (hasMore) =>
+ set(() => ({ hasMore })),
+
+ loadMoreMessages: async () => {
+ // Placeholder for pagination logic
+ const state = get();
+ if (!state.isLoading && state.hasMore && state.currentConversationId) {
+ state.setIsLoading(true);
+ // Load messages via API
+ // then prependMessages
+ state.setIsLoading(false);
+ }
+ },
+
+ // Pinned messages
+ pinMessage: (messageId) =>
+ set((state) => ({
+ messages: state.messages.map((m) =>
+ m.id === messageId ? { ...m, pinned: true } : m
+ ),
+ })),
+
+ unpinMessage: (messageId) =>
+ set((state) => ({
+ messages: state.messages.map((m) =>
+ m.id === messageId ? { ...m, pinned: false } : m
+ ),
+ })),
+
+ // Clear all
+ clear: () =>
+ set(() => ({
+ messages: [],
+ typingUsers: [],
+ editingMessageId: null,
+ editingContent: '',
+ currentConversationId: null,
+ isLoading: false,
+ hasMore: true,
+ })),
+}));
diff --git a/astro-site/src/stores/modalStore.js b/astro-site/src/stores/modalStore.js
new file mode 100644
index 0000000..3dcfe13
--- /dev/null
+++ b/astro-site/src/stores/modalStore.js
@@ -0,0 +1,26 @@
+import { create } from 'zustand';
+
+export const useModalStore = create((set) => ({
+ isOpen: false,
+ type: null,
+ data: {},
+
+ onOpen: (type, data = {}) =>
+ set(() => ({
+ isOpen: true,
+ type,
+ data,
+ })),
+
+ onClose: () =>
+ set(() => ({
+ isOpen: false,
+ type: null,
+ data: {},
+ })),
+
+ onDataUpdate: (data) =>
+ set((state) => ({
+ data: { ...state.data, ...data },
+ })),
+}));
diff --git a/astro-site/src/stores/notificationStore.js b/astro-site/src/stores/notificationStore.js
new file mode 100644
index 0000000..adfe991
--- /dev/null
+++ b/astro-site/src/stores/notificationStore.js
@@ -0,0 +1,42 @@
+import { create } from "zustand";
+
+export const useNotificationStore = create((set) => ({
+ notifications: [
+ { id: 1, type: "mention", username: "Trevor", message: "@you check out the new auth system", channelId: "general", timestamp: Date.now() - 60000 },
+ { id: 2, type: "dm", username: "Marcus", message: "Hey, want to hop on voice?", timestamp: Date.now() - 180000 },
+ { id: 3, type: "mention", username: "Sarah", message: "@you your PR is ready for review", channelId: "api-discussion", timestamp: Date.now() - 300000 },
+ ],
+ unreadCount: 3,
+ perChannelMute: {
+ // channelId -> boolean
+ "announcements": false,
+ "general": false,
+ },
+
+ addNotification: (notification) => set((state) => ({
+ notifications: [notification, ...state.notifications],
+ unreadCount: state.unreadCount + 1,
+ })),
+
+ clearNotification: (id) => set((state) => ({
+ notifications: state.notifications.filter((n) => n.id !== id),
+ unreadCount: Math.max(0, state.unreadCount - 1),
+ })),
+
+ clearAllNotifications: () => set({
+ notifications: [],
+ unreadCount: 0,
+ }),
+
+ toggleChannelMute: (channelId) => set((state) => ({
+ perChannelMute: {
+ ...state.perChannelMute,
+ [channelId]: !state.perChannelMute[channelId],
+ },
+ })),
+
+ isChannelMuted: (channelId) => {
+ const state = useNotificationStore.getState();
+ return state.perChannelMute[channelId] === true;
+ },
+}));
diff --git a/astro-site/src/stores/presenceStore.js b/astro-site/src/stores/presenceStore.js
new file mode 100644
index 0000000..53a2589
--- /dev/null
+++ b/astro-site/src/stores/presenceStore.js
@@ -0,0 +1,72 @@
+import { create } from 'zustand';
+
+export const usePresenceStore = create((set, get) => ({
+ onlineUsers: new Map([
+ ['user-1', { id: 'user-1', status: 'online', lastSeen: Date.now() }],
+ ['user-2', { id: 'user-2', status: 'online', lastSeen: Date.now() }],
+ ['user-3', { id: 'user-3', status: 'idle', lastSeen: Date.now() - 600000 }],
+ ['user-4', { id: 'user-4', status: 'offline', lastSeen: Date.now() - 3600000 }],
+ ]),
+
+ typingUsers: new Map(),
+
+ // Current user status
+ status: 'online',
+ customStatus: '',
+
+ setStatus: (newStatus) => set({ status: newStatus }),
+
+ setCustomStatus: (newCustomStatus) => set({ customStatus: newCustomStatus }),
+
+ setUserStatus: (userId, status) =>
+ set((state) => {
+ const newMap = new Map(state.onlineUsers);
+ newMap.set(userId, {
+ id: userId,
+ status,
+ lastSeen: Date.now(),
+ });
+ return { onlineUsers: newMap };
+ }),
+
+ setUserTyping: (channelId, userId, userName) =>
+ set((state) => {
+ const key = `${channelId}:${userId}`;
+ const newMap = new Map(state.typingUsers);
+
+ const timeout = setTimeout(() => {
+ const updatedMap = new Map(state.typingUsers);
+ updatedMap.delete(key);
+ set({ typingUsers: updatedMap });
+ }, 3000);
+
+ newMap.set(key, { userId, userName, timeout });
+ return { typingUsers: newMap };
+ }),
+
+ clearUserTyping: (channelId, userId) =>
+ set((state) => {
+ const key = `${channelId}:${userId}`;
+ const newMap = new Map(state.typingUsers);
+ const entry = newMap.get(key);
+ if (entry) clearTimeout(entry.timeout);
+ newMap.delete(key);
+ return { typingUsers: newMap };
+ }),
+
+ getChannelTypingUsers: (channelId) => {
+ const { typingUsers } = get();
+ const typingList = [];
+ typingUsers.forEach((value, key) => {
+ if (key.startsWith(channelId)) {
+ typingList.push(value);
+ }
+ });
+ return typingList;
+ },
+
+ getUserStatus: (userId) => {
+ const { onlineUsers } = get();
+ return onlineUsers.get(userId) || { id: userId, status: 'offline', lastSeen: null };
+ },
+}));
diff --git a/astro-site/src/stores/serverStore.js b/astro-site/src/stores/serverStore.js
new file mode 100644
index 0000000..7b80c0d
--- /dev/null
+++ b/astro-site/src/stores/serverStore.js
@@ -0,0 +1,62 @@
+import { create } from 'zustand';
+import { serverService } from '../react-app/services/apiService.js';
+
+export const useServerStore = create((set, get) => ({
+ servers: [],
+ currentServerId: null,
+ isLoading: false,
+ error: null,
+
+ // Fetch servers from API
+ fetchServers: async () => {
+ set({ isLoading: true, error: null });
+ try {
+ const response = await serverService.list();
+ const serversList = response.data || response;
+
+ set({
+ servers: Array.isArray(serversList) ? serversList : [],
+ isLoading: false
+ });
+
+ // Set first server as current if not already set
+ if (serversList.length > 0 && !get().currentServerId) {
+ set({ currentServerId: serversList[0].id });
+ }
+ } catch (error) {
+ set({
+ error: error.message,
+ isLoading: false
+ });
+ }
+ },
+
+ setCurrentServer: (serverId) =>
+ set(() => ({
+ currentServerId: serverId,
+ })),
+
+ getCurrentServer: () => {
+ const { servers, currentServerId } = get();
+ return servers.find((s) => s.id === currentServerId);
+ },
+
+ addServer: (server) =>
+ set((state) => ({
+ servers: [...state.servers, server],
+ })),
+
+ removeServer: (serverId) =>
+ set((state) => ({
+ servers: state.servers.filter((s) => s.id !== serverId),
+ currentServerId:
+ state.currentServerId === serverId ? null : state.currentServerId,
+ })),
+
+ updateServer: (serverId, updates) =>
+ set((state) => ({
+ servers: state.servers.map((s) =>
+ s.id === serverId ? { ...s, ...updates } : s
+ ),
+ })),
+}));
diff --git a/astro-site/src/stores/statusStore.js b/astro-site/src/stores/statusStore.js
new file mode 100644
index 0000000..6d9fdef
--- /dev/null
+++ b/astro-site/src/stores/statusStore.js
@@ -0,0 +1,22 @@
+import { create } from "zustand";
+
+export const useStatusStore = create((set) => ({
+ currentStatus: "online", // online, idle, dnd, invisible
+ customStatus: "Building AeThex",
+ lastActivityTime: Date.now(),
+
+ setStatus: (status) => set({ currentStatus: status, lastActivityTime: Date.now() }),
+ setCustomStatus: (status) => set({ customStatus: status }),
+ updateActivityTime: () => set({ lastActivityTime: Date.now() }),
+
+ // Auto-idle after 5 minutes of inactivity
+ checkIdleStatus: () => {
+ set((state) => {
+ const inactiveMs = Date.now() - state.lastActivityTime;
+ if (inactiveMs > 5 * 60 * 1000 && state.currentStatus === "online") {
+ return { currentStatus: "idle" };
+ }
+ return state;
+ });
+ },
+}));
diff --git a/astro-site/src/stores/threadStore.js b/astro-site/src/stores/threadStore.js
new file mode 100644
index 0000000..474a2e7
--- /dev/null
+++ b/astro-site/src/stores/threadStore.js
@@ -0,0 +1,82 @@
+import { create } from "zustand";
+
+export const useThreadStore = create((set) => ({
+ threads: {
+ "msg-1": [
+ { id: "reply-1", author: "Marcus", text: "Great work!", timestamp: Date.now() - 100000 },
+ { id: "reply-2", author: "Sarah", text: "Agreed! This is solid.", timestamp: Date.now() - 50000 },
+ ],
+ },
+ selectedThread: null,
+
+ getThreads: (messageId) => {
+ const state = useThreadStore.getState();
+ return state.threads[messageId] || [];
+ },
+
+ addReplyToThread: (messageId, reply) => set((state) => ({
+ threads: {
+ ...state.threads,
+ [messageId]: [...(state.threads[messageId] || []), reply],
+ },
+ })),
+
+ setSelectedThread: (messageId) => set({ selectedThread: messageId }),
+ clearSelectedThread: () => set({ selectedThread: null }),
+}));
+
+export const usePinnedStore = create((set) => ({
+ pinnedMessages: {
+ "general": [
+ { id: "msg-1", author: "Trevor", text: "Authentication v2 is live!", timestamp: Date.now() - 86400000 },
+ { id: "msg-2", author: "admin", text: "Welcome to Foundation channel", timestamp: Date.now() - 604800000 },
+ ],
+ },
+
+ getPinnedMessages: (channelId) => {
+ const state = usePinnedStore.getState();
+ return state.pinnedMessages[channelId] || [];
+ },
+
+ pinMessage: (channelId, message) => set((state) => ({
+ pinnedMessages: {
+ ...state.pinnedMessages,
+ [channelId]: [...(state.pinnedMessages[channelId] || []), message],
+ },
+ })),
+
+ unpinMessage: (channelId, messageId) => set((state) => ({
+ pinnedMessages: {
+ ...state.pinnedMessages,
+ [channelId]: state.pinnedMessages[channelId]?.filter((m) => m.id !== messageId) || [],
+ },
+ })),
+}));
+
+export const useFriendRequestStore = create((set) => ({
+ friendRequests: [
+ { id: 1, username: "Alex", avatar: "A", status: "pending" },
+ { id: 2, username: "Jordan", avatar: "J", status: "pending" },
+ { id: 3, username: "Casey", avatar: "C", status: "pending" },
+ ],
+ friends: [
+ { id: 101, username: "Trevor", avatar: "T", status: "online" },
+ { id: 102, username: "Marcus", avatar: "M", status: "online" },
+ ],
+
+ acceptFriendRequest: (id) => set((state) => {
+ const request = state.friendRequests.find((r) => r.id === id);
+ return {
+ friendRequests: state.friendRequests.filter((r) => r.id !== id),
+ friends: [...state.friends, { ...request, status: "online" }],
+ };
+ }),
+
+ declineFriendRequest: (id) => set((state) => ({
+ friendRequests: state.friendRequests.filter((r) => r.id !== id),
+ })),
+
+ removeFriend: (id) => set((state) => ({
+ friends: state.friends.filter((f) => f.id !== id),
+ })),
+}));
diff --git a/astro-site/src/stores/userSettingsStore.js b/astro-site/src/stores/userSettingsStore.js
new file mode 100644
index 0000000..2430ffc
--- /dev/null
+++ b/astro-site/src/stores/userSettingsStore.js
@@ -0,0 +1,85 @@
+import { create } from 'zustand';
+import { authService } from '../react-app/services/apiService.js';
+
+export const useUserSettingsStore = create((set) => ({
+ user: null,
+ isLoading: true,
+ error: null,
+
+ settings: {
+ theme: 'dark',
+ notifications: {
+ desktop: true,
+ sound: true,
+ mentions: true,
+ replies: true,
+ },
+ privacy: {
+ showOnlineStatus: true,
+ allowDMs: 'friends',
+ allowFriendRequests: true,
+ },
+ appearance: {
+ compactMode: false,
+ showAnimations: true,
+ fontSize: 'normal',
+ accentColor: 'blue',
+ },
+ },
+
+ // Initialize user from token
+ initializeUser: async () => {
+ set({ isLoading: true, error: null });
+ try {
+ const response = await authService.getMe();
+ set({
+ user: response.data || response,
+ isLoading: false
+ });
+ } catch (error) {
+ set({
+ error: error.message,
+ isLoading: false
+ });
+ }
+ },
+
+ updateUser: (updates) =>
+ set((state) => ({
+ user: state.user ? { ...state.user, ...updates } : null,
+ })),
+
+ updateSettings: (path, value) =>
+ set((state) => {
+ const keys = path.split('.');
+ const newSettings = JSON.parse(JSON.stringify(state.settings));
+ let current = newSettings;
+ for (let i = 0; i < keys.length - 1; i++) {
+ current = current[keys[i]];
+ }
+ current[keys[keys.length - 1]] = value;
+ return { settings: newSettings };
+ }),
+
+ toggleNotification: (key) =>
+ set((state) => ({
+ settings: {
+ ...state.settings,
+ notifications: {
+ ...state.settings.notifications,
+ [key]: !state.settings.notifications[key],
+ },
+ },
+ })),
+
+ setTheme: (theme) =>
+ set((state) => ({
+ settings: {
+ ...state.settings,
+ appearance: {
+ ...state.settings.appearance,
+ theme,
+ },
+ },
+ })),
+}));
diff --git a/astro-site/src/utils/cn.js b/astro-site/src/utils/cn.js
new file mode 100644
index 0000000..3a07a01
--- /dev/null
+++ b/astro-site/src/utils/cn.js
@@ -0,0 +1,10 @@
+/**
+ * cn utility
+ * Combines classnames with clsx
+ */
+
+import clsx from 'clsx';
+
+export function cn(...inputs) {
+ return clsx(inputs);
+}
diff --git a/astro-site/tailwind.config.mjs b/astro-site/tailwind.config.mjs
index f4f6b02..e85dcc9 100644
--- a/astro-site/tailwind.config.mjs
+++ b/astro-site/tailwind.config.mjs
@@ -12,9 +12,23 @@ export default {
card: '#0f0f0f',
elevated: '#1a1a1a',
},
+ // Trinity Division Colors
+ foundation: {
+ DEFAULT: '#ff0000',
+ dark: '#990000',
+ },
+ corporation: {
+ DEFAULT: '#0066ff',
+ dark: '#003380',
+ },
+ labs: {
+ DEFAULT: '#ffa500',
+ dark: '#ff8c00',
+ },
},
fontFamily: {
sans: ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
+ mono: ['Roboto Mono', 'monospace'],
},
},
},
diff --git a/package-lock.json b/package-lock.json
index be4125c..917ce1c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,10 +24,12 @@
"express-rate-limit": "^7.1.5",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.3",
+ "livekit-server-sdk": "^0.5.10",
"pg": "^8.11.3",
"socket.io": "^4.8.3",
"socket.io-client": "^4.8.3",
- "stripe": "^14.25.0"
+ "stripe": "^14.25.0",
+ "uuid": "^13.0.0"
},
"devDependencies": {
"jest": "^29.7.0",
@@ -179,19 +181,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.27.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
- "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.27.3"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-compilation-targets": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
@@ -219,108 +208,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
- "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.3",
- "@babel/helper-member-expression-to-functions": "^7.28.5",
- "@babel/helper-optimise-call-expression": "^7.27.1",
- "@babel/helper-replace-supers": "^7.28.6",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
- "@babel/traverse": "^7.28.6",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
- "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.3",
- "regexpu-core": "^6.3.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.6.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz",
- "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "debug": "^4.4.3",
- "lodash.debounce": "^4.0.8",
- "resolve": "^1.22.11"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
@@ -331,20 +218,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
- "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.28.5",
- "@babel/types": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-module-imports": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
@@ -377,19 +250,6 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
- "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-plugin-utils": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
@@ -400,56 +260,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-remap-async-to-generator": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz",
- "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.1",
- "@babel/helper-wrap-function": "^7.27.1",
- "@babel/traverse": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-replace-supers": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
- "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-member-expression-to-functions": "^7.28.5",
- "@babel/helper-optimise-call-expression": "^7.27.1",
- "@babel/traverse": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
- "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
@@ -480,21 +290,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-wrap-function": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz",
- "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/template": "^7.28.6",
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helpers": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
@@ -525,103 +320,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
- "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/traverse": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
- "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
- "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
- "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
- "@babel/plugin-transform-optional-chaining": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.13.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
- "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/traverse": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -677,22 +375,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-syntax-import-assertions": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
- "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/plugin-syntax-import-attributes": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
@@ -877,682 +559,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
- "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-arrow-functions": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
- "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz",
- "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-remap-async-to-generator": "^7.27.1",
- "@babel/traverse": "^7.29.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-to-generator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz",
- "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-remap-async-to-generator": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoped-functions": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
- "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz",
- "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz",
- "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-static-block": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
- "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.12.0"
- }
- },
- "node_modules/@babel/plugin-transform-classes": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz",
- "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.3",
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-globals": "^7.28.0",
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-replace-supers": "^7.28.6",
- "@babel/traverse": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-computed-properties": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz",
- "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/template": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
- "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/traverse": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-dotall-regex": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
- "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-duplicate-keys": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
- "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz",
- "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-dynamic-import": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
- "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-explicit-resource-management": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
- "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/plugin-transform-destructuring": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-exponentiation-operator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
- "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-export-namespace-from": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
- "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-for-of": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
- "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-function-name": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
- "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/traverse": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-json-strings": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
- "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-literals": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
- "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz",
- "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-member-expression-literals": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
- "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-amd": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
- "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-commonjs": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz",
- "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
- "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-validator-identifier": "^7.28.5",
- "@babel/traverse": "^7.29.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-umd": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
- "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz",
- "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-new-target": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
- "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz",
- "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-numeric-separator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz",
- "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-rest-spread": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
- "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/plugin-transform-destructuring": "^7.28.5",
- "@babel/plugin-transform-parameters": "^7.27.7",
- "@babel/traverse": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-super": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
- "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/helper-replace-supers": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-catch-binding": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz",
- "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz",
- "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-parameters": {
- "version": "7.27.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz",
- "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz",
- "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-property-in-object": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz",
- "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.3",
- "@babel/helper-create-class-features-plugin": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-property-literals": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
- "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
@@ -1585,318 +591,10 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz",
- "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-regexp-modifiers": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
- "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-reserved-words": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
- "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-shorthand-properties": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
- "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-spread": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz",
- "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-sticky-regex": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz",
- "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-template-literals": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
- "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-typeof-symbol": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
- "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-escapes": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
- "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-property-regex": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
- "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-regex": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
- "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
- "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.28.5",
- "@babel/helper-plugin-utils": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/preset-env": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz",
- "integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.29.0",
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "@babel/helper-validator-option": "^7.27.1",
- "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
- "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
- "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
- "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
- "@babel/plugin-syntax-import-assertions": "^7.28.6",
- "@babel/plugin-syntax-import-attributes": "^7.28.6",
- "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
- "@babel/plugin-transform-arrow-functions": "^7.27.1",
- "@babel/plugin-transform-async-generator-functions": "^7.29.0",
- "@babel/plugin-transform-async-to-generator": "^7.28.6",
- "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
- "@babel/plugin-transform-block-scoping": "^7.28.6",
- "@babel/plugin-transform-class-properties": "^7.28.6",
- "@babel/plugin-transform-class-static-block": "^7.28.6",
- "@babel/plugin-transform-classes": "^7.28.6",
- "@babel/plugin-transform-computed-properties": "^7.28.6",
- "@babel/plugin-transform-destructuring": "^7.28.5",
- "@babel/plugin-transform-dotall-regex": "^7.28.6",
- "@babel/plugin-transform-duplicate-keys": "^7.27.1",
- "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0",
- "@babel/plugin-transform-dynamic-import": "^7.27.1",
- "@babel/plugin-transform-explicit-resource-management": "^7.28.6",
- "@babel/plugin-transform-exponentiation-operator": "^7.28.6",
- "@babel/plugin-transform-export-namespace-from": "^7.27.1",
- "@babel/plugin-transform-for-of": "^7.27.1",
- "@babel/plugin-transform-function-name": "^7.27.1",
- "@babel/plugin-transform-json-strings": "^7.28.6",
- "@babel/plugin-transform-literals": "^7.27.1",
- "@babel/plugin-transform-logical-assignment-operators": "^7.28.6",
- "@babel/plugin-transform-member-expression-literals": "^7.27.1",
- "@babel/plugin-transform-modules-amd": "^7.27.1",
- "@babel/plugin-transform-modules-commonjs": "^7.28.6",
- "@babel/plugin-transform-modules-systemjs": "^7.29.0",
- "@babel/plugin-transform-modules-umd": "^7.27.1",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
- "@babel/plugin-transform-new-target": "^7.27.1",
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
- "@babel/plugin-transform-numeric-separator": "^7.28.6",
- "@babel/plugin-transform-object-rest-spread": "^7.28.6",
- "@babel/plugin-transform-object-super": "^7.27.1",
- "@babel/plugin-transform-optional-catch-binding": "^7.28.6",
- "@babel/plugin-transform-optional-chaining": "^7.28.6",
- "@babel/plugin-transform-parameters": "^7.27.7",
- "@babel/plugin-transform-private-methods": "^7.28.6",
- "@babel/plugin-transform-private-property-in-object": "^7.28.6",
- "@babel/plugin-transform-property-literals": "^7.27.1",
- "@babel/plugin-transform-regenerator": "^7.29.0",
- "@babel/plugin-transform-regexp-modifiers": "^7.28.6",
- "@babel/plugin-transform-reserved-words": "^7.27.1",
- "@babel/plugin-transform-shorthand-properties": "^7.27.1",
- "@babel/plugin-transform-spread": "^7.28.6",
- "@babel/plugin-transform-sticky-regex": "^7.27.1",
- "@babel/plugin-transform-template-literals": "^7.27.1",
- "@babel/plugin-transform-typeof-symbol": "^7.27.1",
- "@babel/plugin-transform-unicode-escapes": "^7.27.1",
- "@babel/plugin-transform-unicode-property-regex": "^7.28.6",
- "@babel/plugin-transform-unicode-regex": "^7.27.1",
- "@babel/plugin-transform-unicode-sets-regex": "^7.28.6",
- "@babel/preset-modules": "0.1.6-no-external-plugins",
- "babel-plugin-polyfill-corejs2": "^0.4.15",
- "babel-plugin-polyfill-corejs3": "^0.14.0",
- "babel-plugin-polyfill-regenerator": "^0.6.6",
- "core-js-compat": "^3.48.0",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/preset-env/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/preset-modules": {
- "version": "0.1.6-no-external-plugins",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
- "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/types": "^7.4.4",
- "esutils": "^2.0.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
- }
- },
"node_modules/@babel/runtime": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -2544,132 +1242,6 @@
"dev": true,
"license": "BSD-3-Clause"
},
- "node_modules/@isaacs/balanced-match": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
- "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/@isaacs/brace-expansion": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
- "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@isaacs/balanced-match": "^4.0.1"
- },
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@isaacs/cliui/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
- "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -3027,6 +1599,7 @@
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
@@ -3142,6 +1715,70 @@
"@noble/hashes": "^1.1.5"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/@reduxjs/toolkit": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
@@ -3184,101 +1821,10 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@rollup/plugin-node-resolve": {
- "version": "15.3.1",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
- "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@rollup/pluginutils": "^5.0.1",
- "@types/resolve": "1.20.2",
- "deepmerge": "^4.2.2",
- "is-module": "^1.0.0",
- "resolve": "^1.22.1"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^2.78.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/plugin-terser": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
- "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "serialize-javascript": "^6.0.1",
- "smob": "^1.0.0",
- "terser": "^5.17.4"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/pluginutils": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
- "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0",
- "estree-walker": "^2.0.2",
- "picomatch": "^4.0.2"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@rollup/pluginutils/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
- "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -3290,9 +1836,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
- "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -3304,9 +1850,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
- "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -3318,9 +1864,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
- "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -3332,9 +1878,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
- "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -3346,9 +1892,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
- "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -3360,9 +1906,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
- "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -3374,9 +1920,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
- "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -3388,9 +1934,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
- "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -3402,9 +1948,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
- "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -3416,9 +1962,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
- "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
@@ -3430,9 +1976,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
- "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"cpu": [
"loong64"
],
@@ -3444,9 +1990,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
- "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
@@ -3458,9 +2004,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
- "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -3472,9 +2018,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
- "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -3486,9 +2032,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
- "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -3500,9 +2046,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
- "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -3514,9 +2060,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -3528,9 +2074,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
- "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -3542,9 +2088,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
- "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
@@ -3556,9 +2102,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
- "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"cpu": [
"arm64"
],
@@ -3570,9 +2116,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
- "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -3584,9 +2130,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
- "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -3598,9 +2144,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"cpu": [
"x64"
],
@@ -3612,9 +2158,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
- "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -3750,29 +2296,6 @@
"node": ">=20.0.0"
}
},
- "node_modules/@surma/rollup-plugin-off-main-thread": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
- "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "ejs": "^3.1.6",
- "json5": "^2.2.0",
- "magic-string": "^0.25.0",
- "string.prototype.matchall": "^4.0.6"
- }
- },
- "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/magic-string": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
- "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sourcemap-codec": "^1.4.8"
- }
- },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -3885,6 +2408,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "25.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz",
@@ -3930,13 +2459,6 @@
"@types/react": "^18.0.0"
}
},
- "node_modules/@types/resolve": {
- "version": "1.20.2",
- "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
- "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
@@ -3951,13 +2473,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/trusted-types": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
- "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
@@ -4343,109 +2858,6 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
- "node_modules/@vitest/expect": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz",
- "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/spy": "1.6.1",
- "@vitest/utils": "1.6.1",
- "chai": "^4.3.10"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/runner": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz",
- "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/utils": "1.6.1",
- "p-limit": "^5.0.0",
- "pathe": "^1.1.1"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/runner/node_modules/p-limit": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz",
- "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "yocto-queue": "^1.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@vitest/runner/node_modules/yocto-queue": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
- "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@vitest/snapshot": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz",
- "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "magic-string": "^0.30.5",
- "pathe": "^1.1.1",
- "pretty-format": "^29.7.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/spy": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz",
- "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tinyspy": "^2.2.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/utils": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz",
- "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "diff-sequences": "^29.6.3",
- "estree-walker": "^3.0.3",
- "loupe": "^2.3.7",
- "pretty-format": "^29.7.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -4466,9 +2878,9 @@
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -4489,19 +2901,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
- "node_modules/acorn-walk": {
- "version": "8.3.4",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
- "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "acorn": "^8.11.0"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/aes-js": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
@@ -4544,9 +2943,9 @@
"license": "MIT"
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4659,23 +3058,6 @@
"sprintf-js": "~1.0.2"
}
},
- "node_modules/array-buffer-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
- "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "is-array-buffer": "^3.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -4692,28 +3074,6 @@
"node": ">=8"
}
},
- "node_modules/arraybuffer.prototype.slice": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
- "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.1",
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "is-array-buffer": "^3.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -4721,53 +3081,16 @@
"dev": true,
"license": "MIT"
},
- "node_modules/assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/async": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
- "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/async-function": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
- "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
- "node_modules/at-least-node": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
- "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
"node_modules/autoprefixer": {
- "version": "10.4.24",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz",
- "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==",
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
"dev": true,
"funding": [
{
@@ -4786,7 +3109,7 @@
"license": "MIT",
"dependencies": {
"browserslist": "^4.28.1",
- "caniuse-lite": "^1.0.30001766",
+ "caniuse-lite": "^1.0.30001774",
"fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
@@ -4801,22 +3124,6 @@
"postcss": "^8.1.0"
}
},
- "node_modules/available-typed-arrays": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "possible-typed-array-names": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/axios": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
@@ -4910,58 +3217,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.15",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz",
- "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.28.6",
- "@babel/helper-define-polyfill-provider": "^0.6.6",
- "semver": "^6.3.1"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz",
- "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.6",
- "core-js-compat": "^3.48.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-regenerator": {
- "version": "0.6.6",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz",
- "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.6"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
"node_modules/babel-preset-current-node-syntax": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
@@ -5181,35 +3436,6 @@
"node": ">= 0.8"
}
},
- "node_modules/cac": {
- "version": "6.7.14",
- "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
- "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
- "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.0",
- "es-define-property": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -5269,10 +3495,52 @@
"node": ">= 6"
}
},
+ "node_modules/camelcase-keys": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz",
+ "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==",
+ "license": "MIT",
+ "dependencies": {
+ "camelcase": "^6.3.0",
+ "map-obj": "^4.1.0",
+ "quick-lru": "^5.1.1",
+ "type-fest": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase-keys/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase-keys/node_modules/type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/caniuse-lite": {
- "version": "1.0.30001767",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz",
- "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==",
+ "version": "1.0.30001775",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
+ "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
"dev": true,
"funding": [
{
@@ -5290,35 +3558,6 @@
],
"license": "CC-BY-4.0"
},
- "node_modules/chai": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
- "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assertion-error": "^1.1.0",
- "check-error": "^1.0.3",
- "deep-eql": "^4.1.3",
- "get-func-name": "^2.0.2",
- "loupe": "^2.3.6",
- "pathval": "^1.1.1",
- "type-detect": "^4.1.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/chai/node_modules/type-detect": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
- "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5346,19 +3585,6 @@
"node": ">=10"
}
},
- "node_modules/check-error": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
- "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-func-name": "^2.0.2"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -5431,6 +3657,15 @@
"node": ">=12"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -5500,16 +3735,6 @@
"node": ">= 6"
}
},
- "node_modules/common-tags": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
- "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
"node_modules/component-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
@@ -5526,13 +3751,6 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
- "node_modules/confbox": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
- "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -5589,20 +3807,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/core-js-compat": {
- "version": "3.48.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz",
- "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.28.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
"node_modules/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
@@ -5657,16 +3861,6 @@
"node": ">= 8"
}
},
- "node_modules/crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -5687,58 +3881,20 @@
"devOptional": true,
"license": "MIT"
},
- "node_modules/data-view-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
- "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
- "dev": true,
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
+ "@babel/runtime": "^7.21.0"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">=0.11"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/data-view-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
- "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/inspect-js"
- }
- },
- "node_modules/data-view-byte-offset": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
- "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
}
},
"node_modules/debug": {
@@ -5765,19 +3921,6 @@
}
}
},
- "node_modules/deep-eql": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
- "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "type-detect": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -5795,42 +3938,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -5971,13 +4078,6 @@
"node": ">= 0.4"
}
},
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -5993,22 +4093,6 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
- "node_modules/ejs": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
- "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "jake": "^10.8.5"
- },
- "bin": {
- "ejs": "bin/cli.js"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/electron-to-chromium": {
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
@@ -6184,75 +4268,6 @@
"is-arrayish": "^0.2.1"
}
},
- "node_modules/es-abstract": {
- "version": "1.24.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
- "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.2",
- "arraybuffer.prototype.slice": "^1.0.4",
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "data-view-buffer": "^1.0.2",
- "data-view-byte-length": "^1.0.2",
- "data-view-byte-offset": "^1.0.1",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "es-set-tostringtag": "^2.1.0",
- "es-to-primitive": "^1.3.0",
- "function.prototype.name": "^1.1.8",
- "get-intrinsic": "^1.3.0",
- "get-proto": "^1.0.1",
- "get-symbol-description": "^1.1.0",
- "globalthis": "^1.0.4",
- "gopd": "^1.2.0",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "internal-slot": "^1.1.0",
- "is-array-buffer": "^3.0.5",
- "is-callable": "^1.2.7",
- "is-data-view": "^1.0.2",
- "is-negative-zero": "^2.0.3",
- "is-regex": "^1.2.1",
- "is-set": "^2.0.3",
- "is-shared-array-buffer": "^1.0.4",
- "is-string": "^1.1.1",
- "is-typed-array": "^1.1.15",
- "is-weakref": "^1.1.1",
- "math-intrinsics": "^1.1.0",
- "object-inspect": "^1.13.4",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.7",
- "own-keys": "^1.0.1",
- "regexp.prototype.flags": "^1.5.4",
- "safe-array-concat": "^1.1.3",
- "safe-push-apply": "^1.0.0",
- "safe-regex-test": "^1.1.0",
- "set-proto": "^1.0.0",
- "stop-iteration-iterator": "^1.1.0",
- "string.prototype.trim": "^1.2.10",
- "string.prototype.trimend": "^1.0.9",
- "string.prototype.trimstart": "^1.0.8",
- "typed-array-buffer": "^1.0.3",
- "typed-array-byte-length": "^1.0.3",
- "typed-array-byte-offset": "^1.0.4",
- "typed-array-length": "^1.0.7",
- "unbox-primitive": "^1.1.0",
- "which-typed-array": "^1.1.19"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -6298,24 +4313,6 @@
"node": ">= 0.4"
}
},
- "node_modules/es-to-primitive": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
- "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7",
- "is-date-object": "^1.0.5",
- "is-symbol": "^1.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -6657,16 +4654,6 @@
"node": ">=4.0"
}
},
- "node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -6913,23 +4900,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
- },
"node_modules/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -6963,39 +4933,6 @@
"node": "^10.12.0 || >=12.0.0"
}
},
- "node_modules/filelist": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
- "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "minimatch": "^5.0.1"
- }
- },
- "node_modules/filelist/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/filelist/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -7083,52 +5020,6 @@
}
}
},
- "node_modules/for-each": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
- "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/foreground-child": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
- "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.6",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/foreground-child/node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
@@ -7193,22 +5084,6 @@
"node": ">= 0.6"
}
},
- "node_modules/fs-extra": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
@@ -7269,37 +5144,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/function.prototype.name": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
- "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "functions-have-names": "^1.2.3",
- "hasown": "^2.0.2",
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -7321,16 +5165,6 @@
"node": ">=10"
}
},
- "node_modules/generator-function": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
- "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -7351,16 +5185,6 @@
"node": "6.* || 8.* || >= 10.*"
}
},
- "node_modules/get-func-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
- "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -7385,13 +5209,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-own-enumerable-property-symbols": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
- "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -7428,24 +5245,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/get-symbol-description": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
- "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -7509,23 +5308,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/globalthis": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-properties": "^1.2.1",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -7573,19 +5355,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/has-bigints": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
- "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -7596,35 +5365,6 @@
"node": ">=8"
}
},
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-proto": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
- "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -7773,12 +5513,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/idb": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
- "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
- "license": "ISC"
- },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -7880,21 +5614,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
- "node_modules/internal-slot": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
- "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "hasown": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -7904,24 +5623,6 @@
"node": ">= 0.10"
}
},
- "node_modules/is-array-buffer": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
- "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -7929,42 +5630,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/is-async-function": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
- "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "async-function": "^1.0.0",
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.1",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-bigint": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
- "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-bigints": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -7978,36 +5643,6 @@
"node": ">=8"
}
},
- "node_modules/is-boolean-object": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
- "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -8024,41 +5659,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-data-view": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
- "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "is-typed-array": "^1.1.13"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-date-object": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
- "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -8069,22 +5669,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-finalizationregistry": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
- "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -8104,26 +5688,6 @@
"node": ">=6"
}
},
- "node_modules/is-generator-function": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
- "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.4",
- "generator-function": "^2.0.0",
- "get-proto": "^1.0.1",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -8137,39 +5701,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-map": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
- "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
- "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-negative-zero": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
- "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -8180,33 +5711,6 @@
"node": ">=0.12.0"
}
},
- "node_modules/is-number-object": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
- "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
- "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
@@ -8217,64 +5721,6 @@
"node": ">=8"
}
},
- "node_modules/is-regex": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
- "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-regexp": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
- "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-set": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
- "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-shared-array-buffer": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
- "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -8288,110 +5734,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-string": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
- "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-symbol": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
- "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-symbols": "^1.1.0",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-typed-array": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
- "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "which-typed-array": "^1.1.16"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakmap": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
- "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakref": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
- "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakset": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
- "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -8511,40 +5853,6 @@
"node": ">=8"
}
},
- "node_modules/jackspeak": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
- "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/jake": {
- "version": "10.9.4",
- "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
- "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "async": "^3.2.6",
- "filelist": "^1.0.4",
- "picocolors": "^1.1.1"
- },
- "bin": {
- "jake": "bin/cli.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
@@ -9186,13 +6494,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/json-schema": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
- "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
- "dev": true,
- "license": "(AFL-2.1 OR BSD-3-Clause)"
- },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -9220,29 +6521,6 @@
"node": ">=6"
}
},
- "node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/jsonpointer": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz",
- "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/jsonwebtoken": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
@@ -9371,21 +6649,83 @@
"dev": true,
"license": "MIT"
},
- "node_modules/local-pkg": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz",
- "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==",
- "dev": true,
+ "node_modules/livekit-server-sdk": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-0.5.10.tgz",
+ "integrity": "sha512-vrdH27Xle5ks8rEC6AYU0DQpKeC3r7d/QHLVsCSXRifjOBwy7412ElZ5duj47tZqEso47klmD1kM1zKPlLIMgw==",
+ "license": "Apache 2.0",
+ "dependencies": {
+ "axios": "^0.21.0",
+ "camelcase-keys": "^7.0.0",
+ "jsonwebtoken": "^8.5.1",
+ "protobufjs": "^6.10.2"
+ }
+ },
+ "node_modules/livekit-server-sdk/node_modules/axios": {
+ "version": "0.21.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+ "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"license": "MIT",
"dependencies": {
- "mlly": "^1.7.3",
- "pkg-types": "^1.2.1"
+ "follow-redirects": "^1.14.0"
+ }
+ },
+ "node_modules/livekit-server-sdk/node_modules/jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
},
"engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
+ "node": ">=4",
+ "npm": ">=1.4.28"
+ }
+ },
+ "node_modules/livekit-server-sdk/node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/livekit-server-sdk/node_modules/jws": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
+ "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.2",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/livekit-server-sdk/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/livekit-server-sdk/node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
}
},
"node_modules/locate-path": {
@@ -9401,20 +6741,6 @@
"node": ">=8"
}
},
- "node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -9464,12 +6790,11 @@
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
- "node_modules/lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
- "dev": true,
- "license": "MIT"
+ "node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+ "license": "Apache-2.0"
},
"node_modules/loose-envify": {
"version": "1.4.0",
@@ -9483,16 +6808,6 @@
"loose-envify": "cli.js"
}
},
- "node_modules/loupe": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
- "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-func-name": "^2.0.1"
- }
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -9503,14 +6818,13 @@
"yallist": "^3.0.2"
}
},
- "node_modules/magic-string": {
- "version": "0.30.21",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
+ "node_modules/lucide-react": {
+ "version": "0.294.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.294.0.tgz",
+ "integrity": "sha512-V7o0/VECSGbLHn3/1O67FUgBwWB+hmzshrgDVRJQhMh8uj5D3HBuIvhuAmQTtlupILSplwIZg5FTc4tTKMA2SA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/make-dir": {
@@ -9547,6 +6861,18 @@
"tmpl": "1.0.5"
}
},
+ "node_modules/map-obj": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
+ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -9721,26 +7047,6 @@
"node": ">=10"
}
},
- "node_modules/mlly": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
- "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "acorn": "^8.15.0",
- "pathe": "^2.0.3",
- "pkg-types": "^1.3.1",
- "ufo": "^1.6.1"
- }
- },
- "node_modules/mlly/node_modules/pathe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -9993,37 +7299,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.assign": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
- "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0",
- "has-symbols": "^1.1.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -10079,24 +7354,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/own-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
- "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-intrinsic": "^1.2.6",
- "object-keys": "^1.1.1",
- "safe-push-apply": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -10152,13 +7409,6 @@
"node": ">=6"
}
},
- "node_modules/package-json-from-dist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
- "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
- "dev": true,
- "license": "BlueOak-1.0.0"
- },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -10236,43 +7486,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/path-scurry": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
- "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^11.0.0",
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-scurry/node_modules/lru-cache": {
- "version": "11.2.5",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
- "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/path-scurry/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
@@ -10289,23 +7502,6 @@
"node": ">=8"
}
},
- "node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/pathval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
- "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"node_modules/pg": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
@@ -10449,35 +7645,6 @@
"node": ">=8"
}
},
- "node_modules/pkg-types": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
- "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "confbox": "^0.1.8",
- "mlly": "^1.7.4",
- "pathe": "^2.0.1"
- }
- },
- "node_modules/pkg-types/node_modules/pathe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/possible-typed-array-names": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
- "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -10691,19 +7858,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/pretty-bytes": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
- "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -10746,6 +7900,32 @@
"node": ">= 6"
}
},
+ "node_modules/protobufjs": {
+ "version": "6.11.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+ "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "bin": {
+ "pbjs": "bin/pbjs",
+ "pbts": "bin/pbts"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -10835,14 +8015,16 @@
],
"license": "MIT"
},
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.0"
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/range-parser": {
@@ -11022,108 +8204,6 @@
"redux": "^5.0.0"
}
},
- "node_modules/reflect.getprototypeof": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
- "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.9",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.7",
- "get-proto": "^1.0.1",
- "which-builtin-type": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/regenerate": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/regenerate-unicode-properties": {
- "version": "10.2.2",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
- "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regenerate": "^1.4.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regexp.prototype.flags": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
- "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-errors": "^1.3.0",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "set-function-name": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/regexpu-core": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
- "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.2.2",
- "regjsgen": "^0.8.0",
- "regjsparser": "^0.13.0",
- "unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.2.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regjsgen": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/regjsparser": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz",
- "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "jsesc": "~3.1.0"
- },
- "bin": {
- "regjsparser": "bin/parser"
- }
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -11134,16 +8214,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
@@ -11232,12 +8302,11 @@
}
},
"node_modules/rollup": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
- "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -11249,31 +8318,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.57.1",
- "@rollup/rollup-android-arm64": "4.57.1",
- "@rollup/rollup-darwin-arm64": "4.57.1",
- "@rollup/rollup-darwin-x64": "4.57.1",
- "@rollup/rollup-freebsd-arm64": "4.57.1",
- "@rollup/rollup-freebsd-x64": "4.57.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
- "@rollup/rollup-linux-arm64-gnu": "4.57.1",
- "@rollup/rollup-linux-arm64-musl": "4.57.1",
- "@rollup/rollup-linux-loong64-gnu": "4.57.1",
- "@rollup/rollup-linux-loong64-musl": "4.57.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
- "@rollup/rollup-linux-ppc64-musl": "4.57.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
- "@rollup/rollup-linux-riscv64-musl": "4.57.1",
- "@rollup/rollup-linux-s390x-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-musl": "4.57.1",
- "@rollup/rollup-openbsd-x64": "4.57.1",
- "@rollup/rollup-openharmony-arm64": "4.57.1",
- "@rollup/rollup-win32-arm64-msvc": "4.57.1",
- "@rollup/rollup-win32-ia32-msvc": "4.57.1",
- "@rollup/rollup-win32-x64-gnu": "4.57.1",
- "@rollup/rollup-win32-x64-msvc": "4.57.1",
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
+ "@rollup/rollup-android-arm64": "4.59.0",
+ "@rollup/rollup-darwin-arm64": "4.59.0",
+ "@rollup/rollup-darwin-x64": "4.59.0",
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
+ "@rollup/rollup-freebsd-x64": "4.59.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
+ "@rollup/rollup-openbsd-x64": "4.59.0",
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},
@@ -11301,26 +8370,6 @@
"queue-microtask": "^1.2.2"
}
},
- "node_modules/safe-array-concat": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
- "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "has-symbols": "^1.1.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">=0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -11341,41 +8390,6 @@
],
"license": "MIT"
},
- "node_modules/safe-push-apply": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
- "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safe-regex-test": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
- "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-regex": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -11433,16 +8447,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
"node_modules/serve-static": {
"version": "1.16.3",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
@@ -11464,55 +8468,6 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC"
},
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-function-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-proto": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
- "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -11614,13 +8569,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/siginfo": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -11657,13 +8605,6 @@
"node": ">=8"
}
},
- "node_modules/smob": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
- "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/socket.io": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
@@ -11864,14 +8805,6 @@
"source-map": "^0.6.0"
}
},
- "node_modules/sourcemap-codec": {
- "version": "1.4.8",
- "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "deprecated": "Please use @jridgewell/sourcemap-codec instead",
- "dev": true,
- "license": "MIT"
- },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -11901,13 +8834,6 @@
"node": ">=10"
}
},
- "node_modules/stackback": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
@@ -11917,27 +8843,6 @@
"node": ">= 0.8"
}
},
- "node_modules/std-env": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
- "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/stop-iteration-iterator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
- "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "internal-slot": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -11975,124 +8880,6 @@
"node": ">=8"
}
},
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string.prototype.matchall": {
- "version": "4.0.12",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
- "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.6",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "internal-slot": "^1.1.0",
- "regexp.prototype.flags": "^1.5.3",
- "set-function-name": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trim": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
- "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-data-property": "^1.1.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-object-atoms": "^1.0.0",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimend": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
- "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimstart": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
- "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/stringify-object": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
- "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "get-own-enumerable-property-symbols": "^3.0.0",
- "is-obj": "^1.0.1",
- "is-regexp": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -12105,20 +8892,6 @@
"node": ">=8"
}
},
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
@@ -12129,16 +8902,6 @@
"node": ">=8"
}
},
- "node_modules/strip-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz",
- "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
@@ -12162,26 +8925,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/strip-literal": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz",
- "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "js-tokens": "^9.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
- "node_modules/strip-literal/node_modules/js-tokens": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
- "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/stripe": {
"version": "14.25.0",
"resolved": "https://registry.npmjs.org/stripe/-/stripe-14.25.0.tgz",
@@ -12395,54 +9138,13 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
- "node_modules/temp-dir": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
- "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/tempy": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz",
- "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-stream": "^2.0.0",
- "temp-dir": "^2.0.0",
- "type-fest": "^0.16.0",
- "unique-string": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/tempy/node_modules/type-fest": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
- "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/terser": {
"version": "5.46.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
"integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
"dev": true,
"license": "BSD-2-Clause",
+ "optional": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
@@ -12461,7 +9163,8 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "optional": true
},
"node_modules/terser/node_modules/source-map-support": {
"version": "0.5.21",
@@ -12469,6 +9172,7 @@
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -12519,13 +9223,6 @@
"node": ">=0.8"
}
},
- "node_modules/tinybench": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -12575,26 +9272,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/tinypool": {
- "version": "0.8.4",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz",
- "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tinyspy": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
- "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -12715,84 +9392,6 @@
"node": ">= 0.6"
}
},
- "node_modules/typed-array-buffer": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
- "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/typed-array-byte-length": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
- "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-byte-offset": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
- "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.15",
- "reflect.getprototypeof": "^1.0.9"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-length": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
- "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "is-typed-array": "^1.1.13",
- "possible-typed-array-names": "^1.0.0",
- "reflect.getprototypeof": "^1.0.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -12808,32 +9407,6 @@
"node": ">=14.17"
}
},
- "node_modules/ufo": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
- "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/unbox-primitive": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
- "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.1.0",
- "which-boxed-primitive": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -12847,73 +9420,6 @@
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT"
},
- "node_modules/unicode-canonical-property-names-ecmascript": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
- "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "unicode-canonical-property-names-ecmascript": "^2.0.0",
- "unicode-property-aliases-ecmascript": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-value-ecmascript": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
- "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-property-aliases-ecmascript": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
- "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "crypto-random-string": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -12923,17 +9429,6 @@
"node": ">= 0.8"
}
},
- "node_modules/upath": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
- "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4",
- "yarn": "*"
- }
- },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -12999,6 +9494,19 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/uuid": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
+ "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
"node_modules/v8-to-istanbul": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
@@ -13084,339 +9592,6 @@
}
}
},
- "node_modules/vite-node": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz",
- "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cac": "^6.7.14",
- "debug": "^4.3.4",
- "pathe": "^1.1.1",
- "picocolors": "^1.0.0",
- "vite": "^5.0.0"
- },
- "bin": {
- "vite-node": "vite-node.mjs"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/vite-node/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/vite-node/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/vite-plugin-pwa": {
- "version": "0.17.5",
- "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.17.5.tgz",
- "integrity": "sha512-UxRNPiJBzh4tqU/vc8G2TxmrUTzT6BqvSzhszLk62uKsf+npXdvLxGDz9C675f4BJi6MbD2tPnJhi5txlMzxbQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "pretty-bytes": "^6.1.1",
- "workbox-build": "^7.0.0",
- "workbox-window": "^7.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- },
- "peerDependencies": {
- "vite": "^3.1.0 || ^4.0.0 || ^5.0.0",
- "workbox-build": "^7.0.0",
- "workbox-window": "^7.0.0"
- }
- },
- "node_modules/vite-plugin-pwa/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/vite-plugin-pwa/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/vitest": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz",
- "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/expect": "1.6.1",
- "@vitest/runner": "1.6.1",
- "@vitest/snapshot": "1.6.1",
- "@vitest/spy": "1.6.1",
- "@vitest/utils": "1.6.1",
- "acorn-walk": "^8.3.2",
- "chai": "^4.3.10",
- "debug": "^4.3.4",
- "execa": "^8.0.1",
- "local-pkg": "^0.5.0",
- "magic-string": "^0.30.5",
- "pathe": "^1.1.1",
- "picocolors": "^1.0.0",
- "std-env": "^3.5.0",
- "strip-literal": "^2.0.0",
- "tinybench": "^2.5.1",
- "tinypool": "^0.8.3",
- "vite": "^5.0.0",
- "vite-node": "1.6.1",
- "why-is-node-running": "^2.2.2"
- },
- "bin": {
- "vitest": "vitest.mjs"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "@edge-runtime/vm": "*",
- "@types/node": "^18.0.0 || >=20.0.0",
- "@vitest/browser": "1.6.1",
- "@vitest/ui": "1.6.1",
- "happy-dom": "*",
- "jsdom": "*"
- },
- "peerDependenciesMeta": {
- "@edge-runtime/vm": {
- "optional": true
- },
- "@types/node": {
- "optional": true
- },
- "@vitest/browser": {
- "optional": true
- },
- "@vitest/ui": {
- "optional": true
- },
- "happy-dom": {
- "optional": true
- },
- "jsdom": {
- "optional": true
- }
- }
- },
- "node_modules/vitest/node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/vitest/node_modules/execa": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
- "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^8.0.1",
- "human-signals": "^5.0.0",
- "is-stream": "^3.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^5.1.0",
- "onetime": "^6.0.0",
- "signal-exit": "^4.1.0",
- "strip-final-newline": "^3.0.0"
- },
- "engines": {
- "node": ">=16.17"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/vitest/node_modules/get-stream": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
- "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/human-signals": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
- "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=16.17.0"
- }
- },
- "node_modules/vitest/node_modules/is-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
- "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/mimic-fn": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
- "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/vitest/node_modules/npm-run-path": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
- "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^4.0.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/onetime": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
- "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-fn": "^4.0.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/path-key": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
- "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/vitest/node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/vitest/node_modules/strip-final-newline": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
- "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
@@ -13459,112 +9634,6 @@
"node": ">= 8"
}
},
- "node_modules/which-boxed-primitive": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
- "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-bigint": "^1.1.0",
- "is-boolean-object": "^1.2.1",
- "is-number-object": "^1.1.1",
- "is-string": "^1.1.1",
- "is-symbol": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-builtin-type": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
- "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "function.prototype.name": "^1.1.6",
- "has-tostringtag": "^1.0.2",
- "is-async-function": "^2.0.0",
- "is-date-object": "^1.1.0",
- "is-finalizationregistry": "^1.1.0",
- "is-generator-function": "^1.0.10",
- "is-regex": "^1.2.1",
- "is-weakref": "^1.0.2",
- "isarray": "^2.0.5",
- "which-boxed-primitive": "^1.1.0",
- "which-collection": "^1.0.2",
- "which-typed-array": "^1.1.16"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-collection": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
- "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-map": "^2.0.3",
- "is-set": "^2.0.3",
- "is-weakmap": "^2.0.2",
- "is-weakset": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/which-typed-array": {
- "version": "1.1.20",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
- "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "for-each": "^0.3.5",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/why-is-node-running": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
- "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "siginfo": "^2.0.0",
- "stackback": "0.0.2"
- },
- "bin": {
- "why-is-node-running": "cli.js"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -13584,454 +9653,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/workbox-background-sync": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.0.tgz",
- "integrity": "sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==",
- "license": "MIT",
- "dependencies": {
- "idb": "^7.0.1",
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-broadcast-update": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.0.tgz",
- "integrity": "sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-build": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.4.0.tgz",
- "integrity": "sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@apideck/better-ajv-errors": "^0.3.1",
- "@babel/core": "^7.24.4",
- "@babel/preset-env": "^7.11.0",
- "@babel/runtime": "^7.11.2",
- "@rollup/plugin-babel": "^5.2.0",
- "@rollup/plugin-node-resolve": "^15.2.3",
- "@rollup/plugin-replace": "^2.4.1",
- "@rollup/plugin-terser": "^0.4.3",
- "@surma/rollup-plugin-off-main-thread": "^2.2.3",
- "ajv": "^8.6.0",
- "common-tags": "^1.8.0",
- "fast-json-stable-stringify": "^2.1.0",
- "fs-extra": "^9.0.1",
- "glob": "^11.0.1",
- "lodash": "^4.17.20",
- "pretty-bytes": "^5.3.0",
- "rollup": "^2.79.2",
- "source-map": "^0.8.0-beta.0",
- "stringify-object": "^3.3.0",
- "strip-comments": "^2.0.1",
- "tempy": "^0.6.0",
- "upath": "^1.2.0",
- "workbox-background-sync": "7.4.0",
- "workbox-broadcast-update": "7.4.0",
- "workbox-cacheable-response": "7.4.0",
- "workbox-core": "7.4.0",
- "workbox-expiration": "7.4.0",
- "workbox-google-analytics": "7.4.0",
- "workbox-navigation-preload": "7.4.0",
- "workbox-precaching": "7.4.0",
- "workbox-range-requests": "7.4.0",
- "workbox-recipes": "7.4.0",
- "workbox-routing": "7.4.0",
- "workbox-strategies": "7.4.0",
- "workbox-streams": "7.4.0",
- "workbox-sw": "7.4.0",
- "workbox-window": "7.4.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz",
- "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "json-schema": "^0.4.0",
- "jsonpointer": "^5.0.0",
- "leven": "^3.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "ajv": ">=8"
- }
- },
- "node_modules/workbox-build/node_modules/@rollup/plugin-babel": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
- "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.10.4",
- "@rollup/pluginutils": "^3.1.0"
- },
- "engines": {
- "node": ">= 10.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0",
- "@types/babel__core": "^7.1.9",
- "rollup": "^1.20.0||^2.0.0"
- },
- "peerDependenciesMeta": {
- "@types/babel__core": {
- "optional": true
- }
- }
- },
- "node_modules/workbox-build/node_modules/@rollup/plugin-replace": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz",
- "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@rollup/pluginutils": "^3.1.0",
- "magic-string": "^0.25.7"
- },
- "peerDependencies": {
- "rollup": "^1.20.0 || ^2.0.0"
- }
- },
- "node_modules/workbox-build/node_modules/@rollup/pluginutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
- "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "0.0.39",
- "estree-walker": "^1.0.1",
- "picomatch": "^2.2.2"
- },
- "engines": {
- "node": ">= 8.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0"
- }
- },
- "node_modules/workbox-build/node_modules/@types/estree": {
- "version": "0.0.39",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
- "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/workbox-build/node_modules/ajv": {
- "version": "8.17.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
- "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/workbox-build/node_modules/estree-walker": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
- "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/workbox-build/node_modules/glob": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
- "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "foreground-child": "^3.3.1",
- "jackspeak": "^4.1.1",
- "minimatch": "^10.1.1",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^2.0.0"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/workbox-build/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/workbox-build/node_modules/magic-string": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
- "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sourcemap-codec": "^1.4.8"
- }
- },
- "node_modules/workbox-build/node_modules/minimatch": {
- "version": "10.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz",
- "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/brace-expansion": "^5.0.1"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/workbox-build/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/workbox-build/node_modules/pretty-bytes": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
- "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/workbox-build/node_modules/rollup": {
- "version": "2.79.2",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
- "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=10.0.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/workbox-build/node_modules/source-map": {
- "version": "0.8.0-beta.0",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
- "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
- "deprecated": "The work that was done in this beta branch won't be included in future versions",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "whatwg-url": "^7.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/workbox-build/node_modules/tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/workbox-build/node_modules/webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/workbox-build/node_modules/whatwg-url": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
- "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^1.0.1",
- "webidl-conversions": "^4.0.2"
- }
- },
- "node_modules/workbox-cacheable-response": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.4.0.tgz",
- "integrity": "sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-core": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.4.0.tgz",
- "integrity": "sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==",
- "license": "MIT"
- },
- "node_modules/workbox-expiration": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.4.0.tgz",
- "integrity": "sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==",
- "license": "MIT",
- "dependencies": {
- "idb": "^7.0.1",
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-google-analytics": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.4.0.tgz",
- "integrity": "sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-background-sync": "7.4.0",
- "workbox-core": "7.4.0",
- "workbox-routing": "7.4.0",
- "workbox-strategies": "7.4.0"
- }
- },
- "node_modules/workbox-navigation-preload": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.4.0.tgz",
- "integrity": "sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-precaching": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.4.0.tgz",
- "integrity": "sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==",
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0",
- "workbox-routing": "7.4.0",
- "workbox-strategies": "7.4.0"
- }
- },
- "node_modules/workbox-range-requests": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.4.0.tgz",
- "integrity": "sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-recipes": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.4.0.tgz",
- "integrity": "sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-cacheable-response": "7.4.0",
- "workbox-core": "7.4.0",
- "workbox-expiration": "7.4.0",
- "workbox-precaching": "7.4.0",
- "workbox-routing": "7.4.0",
- "workbox-strategies": "7.4.0"
- }
- },
- "node_modules/workbox-routing": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.4.0.tgz",
- "integrity": "sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==",
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-strategies": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.4.0.tgz",
- "integrity": "sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==",
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0"
- }
- },
- "node_modules/workbox-streams": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.4.0.tgz",
- "integrity": "sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "workbox-core": "7.4.0",
- "workbox-routing": "7.4.0"
- }
- },
- "node_modules/workbox-sw": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.4.0.tgz",
- "integrity": "sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/workbox-window": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.4.0.tgz",
- "integrity": "sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/trusted-types": "^2.0.2",
- "workbox-core": "7.4.0"
- }
- },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -14050,25 +9671,6 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -14186,6 +9788,34 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/zustand": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+ "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+ "license": "MIT",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
"packages/core": {
"name": "@aethex/core",
"version": "1.0.0",
@@ -14222,32 +9852,53 @@
"version": "1.0.0",
"dependencies": {
"@reduxjs/toolkit": "^2.0.1",
+ "axios": "^1.6.2",
+ "clsx": "^2.0.0",
+ "date-fns": "^2.30.0",
+ "libsodium-wrappers": "^0.7.13",
+ "lucide-react": "^0.294.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "^9.0.4",
- "react-router-dom": "^6.21.0",
+ "react-redux": "^9.0.0",
+ "react-router-dom": "^6.20.0",
"socket.io-client": "^4.6.0",
- "workbox-background-sync": "^7.0.0",
- "workbox-expiration": "^7.0.0",
- "workbox-precaching": "^7.0.0",
- "workbox-routing": "^7.0.0",
- "workbox-strategies": "^7.0.0"
+ "zustand": "^4.4.7"
},
"devDependencies": {
- "@types/react": "^18.2.45",
- "@types/react-dom": "^18.2.18",
- "@typescript-eslint/eslint-plugin": "^6.15.0",
- "@typescript-eslint/parser": "^6.15.0",
- "@vitejs/plugin-react": "^4.2.1",
- "autoprefixer": "^10.4.17",
- "eslint": "^8.55.0",
- "postcss": "^8.4.33",
- "tailwindcss": "^3.3.7",
+ "@types/node": "^20.0.0",
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "@typescript-eslint/eslint-plugin": "^6.13.0",
+ "@typescript-eslint/parser": "^6.13.0",
+ "@vitejs/plugin-react": "^4.2.0",
+ "autoprefixer": "^10.4.16",
+ "eslint": "^8.54.0",
+ "postcss": "^8.4.31",
+ "tailwindcss": "^3.3.6",
"typescript": "^5.3.3",
- "vite": "^5.0.8",
- "vite-plugin-pwa": "^0.17.4",
- "vitest": "^1.1.0"
+ "vite": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=9.0.0"
}
+ },
+ "packages/web/node_modules/@types/node": {
+ "version": "20.19.35",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.35.tgz",
+ "integrity": "sha512-Uarfe6J91b9HAUXxjvSOdiO2UPOKLm07Q1oh0JHxoZ1y8HoqxDAu3gVrsrOHeiio0kSsoVBt4wFrKOm0dKxVPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "packages/web/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index 47acbcb..c53993e 100644
--- a/package.json
+++ b/package.json
@@ -55,10 +55,12 @@
"express-rate-limit": "^7.1.5",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.3",
+ "livekit-server-sdk": "^0.5.10",
"pg": "^8.11.3",
"socket.io": "^4.8.3",
"socket.io-client": "^4.8.3",
- "stripe": "^14.25.0"
+ "stripe": "^14.25.0",
+ "uuid": "^13.0.0"
},
"devDependencies": {
"jest": "^29.7.0",
diff --git a/packages/web/.env.example b/packages/web/.env.example
new file mode 100644
index 0000000..37b8214
--- /dev/null
+++ b/packages/web/.env.example
@@ -0,0 +1,3 @@
+VITE_API_URL=http://localhost:3000
+VITE_APP_NAME=AeThex Connect
+VITE_APP_URL=http://localhost:5173
diff --git a/packages/web/index.html b/packages/web/index.html
new file mode 100644
index 0000000..2e3dea0
--- /dev/null
+++ b/packages/web/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
AeThex Connect
+
+
+
+
+
+
diff --git a/packages/web/package.json b/packages/web/package.json
new file mode 100644
index 0000000..099f40b
--- /dev/null
+++ b/packages/web/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@aethex/web",
+ "version": "1.0.0",
+ "description": "AeThex Connect - Main Web Application",
+ "type": "module",
+ "main": "dist/index.js",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview",
+ "lint": "eslint src --ext .ts,.tsx",
+ "type-check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.20.0",
+ "@reduxjs/toolkit": "^2.0.1",
+ "react-redux": "^9.0.0",
+ "axios": "^1.6.2",
+ "socket.io-client": "^4.6.0",
+ "libsodium-wrappers": "^0.7.13",
+ "zustand": "^4.4.7",
+ "clsx": "^2.0.0",
+ "date-fns": "^2.30.0",
+ "lucide-react": "^0.294.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "@types/node": "^20.0.0",
+ "typescript": "^5.3.3",
+ "vite": "^5.0.0",
+ "@vitejs/plugin-react": "^4.2.0",
+ "tailwindcss": "^3.3.6",
+ "postcss": "^8.4.31",
+ "autoprefixer": "^10.4.16",
+ "eslint": "^8.54.0",
+ "@typescript-eslint/eslint-plugin": "^6.13.0",
+ "@typescript-eslint/parser": "^6.13.0"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=9.0.0"
+ }
+}
diff --git a/packages/web/postcss.config.cjs b/packages/web/postcss.config.cjs
new file mode 100644
index 0000000..33ad091
--- /dev/null
+++ b/packages/web/postcss.config.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx
new file mode 100644
index 0000000..4f1f6ac
--- /dev/null
+++ b/packages/web/src/App.tsx
@@ -0,0 +1,95 @@
+import { useEffect } from 'react'
+import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
+import { useAppDispatch, useAppSelector } from '@/hooks/useRedux'
+import { setUser } from '@/stores/authSlice'
+import { authService } from '@/services/api'
+
+import LoginPage from './pages/Login'
+import SignupPage from './pages/Signup'
+import ChatPage from './pages/Chat'
+import CallsPage from './pages/Calls'
+import SettingsPage from './pages/Settings'
+import AdminPage from './pages/Admin'
+import ProfilePage from './pages/Profile'
+import FriendsPage from './pages/Friends'
+import ProtectedRoute from '@/components/ProtectedRoute'
+
+function App() {
+ const dispatch = useAppDispatch()
+ const { isAuthenticated, isLoading } = useAppSelector((state) => state.auth)
+
+ useEffect(() => {
+ // Check if user is already logged in
+ const checkAuth = async () => {
+ const token = localStorage.getItem('token')
+ if (token) {
+ try {
+ const response = await authService.getMe()
+ dispatch(setUser(response.data))
+ } catch (error) {
+ localStorage.removeItem('token')
+ }
+ }
+ }
+
+ checkAuth()
+ }, [dispatch])
+
+ if (isLoading) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ {/* Public Routes */}
+ : }
+ />
+ : }
+ />
+
+ {/* Protected Routes */}
+ } />}
+ />
+ } />}
+ />
+ } />}
+ />
+ } />}
+ />
+ } />}
+ />
+ } />}
+ />
+
+ {/* Default redirect */}
+ } />
+
+
+ )
+}
+
+export default App
diff --git a/packages/web/src/components/ChannelList.tsx b/packages/web/src/components/ChannelList.tsx
new file mode 100644
index 0000000..6d48a49
--- /dev/null
+++ b/packages/web/src/components/ChannelList.tsx
@@ -0,0 +1,100 @@
+import type { Channel } from '@/types'
+import { Hash, Volume2, Plus } from 'lucide-react'
+
+interface ChannelListProps {
+ channels: Channel[]
+ selectedChannelId: string | null
+ onSelectChannel: (channelId: string) => void
+ serverName: string
+}
+
+export default function ChannelList({
+ channels,
+ selectedChannelId,
+ onSelectChannel,
+ serverName,
+}: ChannelListProps) {
+ const textChannels = channels.filter((c) => c.channel_type === 'text')
+ const voiceChannels = channels.filter((c) => c.channel_type === 'voice')
+
+ return (
+
+ {/* Server Header */}
+
+
{serverName}
+
+
+ {/* Channels */}
+
+ {/* Text Channels */}
+ {textChannels.length > 0 && (
+
+
+
+ {textChannels.map((channel) => (
+
+ ))}
+
+
+ )}
+
+ {/* Voice Channels */}
+ {voiceChannels.length > 0 && (
+
+
+
+ {voiceChannels.map((channel) => (
+
+ ))}
+
+
+ )}
+
+
+ {/* User Profile at bottom */}
+
+
+ )
+}
diff --git a/packages/web/src/components/ChatArea.tsx b/packages/web/src/components/ChatArea.tsx
new file mode 100644
index 0000000..d1e2dd1
--- /dev/null
+++ b/packages/web/src/components/ChatArea.tsx
@@ -0,0 +1,93 @@
+import { useState } from 'react'
+import type { Message } from '@/types'
+import { Send, Paperclip, Smile } from 'lucide-react'
+import MessageBubble from './MessageBubble'
+
+interface ChatAreaProps {
+ channelId: string
+ messages: Message[]
+}
+
+export default function ChatArea({ channelId, messages }: ChatAreaProps) {
+ const [messageInput, setMessageInput] = useState('')
+ const [isSending, setIsSending] = useState(false)
+
+ const handleSendMessage = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (!messageInput.trim()) return
+
+ setIsSending(true)
+ try {
+ // TODO: Call messageService.send()
+ setMessageInput('')
+ } finally {
+ setIsSending(false)
+ }
+ }
+
+ if (!channelId) {
+ return (
+
+
Select a channel to start chatting
+
+ )
+ }
+
+ return (
+
+ {/* Messages */}
+
+ {messages.length === 0 ? (
+
+ ) : (
+ messages.map((message) => (
+
+ ))
+ )}
+
+
+ {/* Message Input */}
+
+
+ )
+}
diff --git a/packages/web/src/components/MemberList.tsx b/packages/web/src/components/MemberList.tsx
new file mode 100644
index 0000000..9e49bc4
--- /dev/null
+++ b/packages/web/src/components/MemberList.tsx
@@ -0,0 +1,76 @@
+import { useState } from 'react'
+import type { User } from '@/types'
+import { Users, Search } from 'lucide-react'
+
+interface MemberListProps {
+ serverId: string
+}
+
+export default function MemberList({ serverId: _serverId }: MemberListProps) {
+ const [members] = useState
([])
+ const [searchQuery, setSearchQuery] = useState('')
+
+ // TODO: Load members from API
+
+ const filteredMembers = members.filter((m) =>
+ m.display_name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ m.username?.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+
+ return (
+
+ {/* Header */}
+
+
+
Members
+
+
+ {/* Search */}
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="Search members..."
+ className="flex-1 bg-transparent text-white text-sm outline-none placeholder-gray-500"
+ />
+
+
+
+ {/* Members List */}
+
+ {filteredMembers.length === 0 ? (
+
No members found
+ ) : (
+
+ {filteredMembers.map((member) => (
+
+

+
+
+ {member.display_name}
+
+
+ {member.status === 'online' && '🟢 Online'}
+ {member.status === 'away' && '🟡 Away'}
+ {member.status === 'dnd' && '🔴 Do Not Disturb'}
+ {member.status === 'offline' && '⚫ Offline'}
+
+
+
+ ))}
+
+ )}
+
+
+ )
+}
diff --git a/packages/web/src/components/MessageBubble.tsx b/packages/web/src/components/MessageBubble.tsx
new file mode 100644
index 0000000..78ac261
--- /dev/null
+++ b/packages/web/src/components/MessageBubble.tsx
@@ -0,0 +1,29 @@
+import type { Message } from '@/types'
+
+interface MessageBubbleProps {
+ message: Message
+}
+
+export default function MessageBubble({ message }: MessageBubbleProps) {
+ const senderName = message.user?.display_name || message.user?.username || 'Unknown'
+ const avatar = message.user?.avatar_url || 'https://via.placeholder.com/40'
+
+ return (
+
+

+
+
+ {senderName}
+
+ {new Date(message.created_at).toLocaleTimeString()}
+
+
+
{message.content}
+
+
+ )
+}
diff --git a/packages/web/src/components/NotificationsPanel.tsx b/packages/web/src/components/NotificationsPanel.tsx
new file mode 100644
index 0000000..2d95d5e
--- /dev/null
+++ b/packages/web/src/components/NotificationsPanel.tsx
@@ -0,0 +1,231 @@
+import { useEffect, useState } from 'react'
+import { useAppSelector } from '@/hooks/useRedux'
+import { notificationService } from '@/services/api'
+
+interface Notification {
+ id: string
+ type: 'friend_request' | 'message' | 'call' | 'server_invite' | 'system'
+ title: string
+ message: string
+ read: boolean
+ action_url?: string
+ created_at: string
+}
+
+interface NotificationsProps {
+ isOpen: boolean
+ onClose: () => void
+}
+
+export default function NotificationsPanel({ isOpen, onClose }: NotificationsProps) {
+ const { user } = useAppSelector((state) => state.auth)
+ const [notifications, setNotifications] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [filter, setFilter] = useState<'all' | 'unread'>('unread')
+
+ useEffect(() => {
+ if (isOpen && user) {
+ loadNotifications()
+ }
+ }, [isOpen, user])
+
+ const loadNotifications = async () => {
+ setLoading(true)
+ try {
+ const response = await notificationService.list()
+ setNotifications(response.data)
+ } catch (error) {
+ console.error('Failed to load notifications:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleMarkAsRead = async (notificationId: string) => {
+ try {
+ await notificationService.markAsRead(notificationId)
+ setNotifications((prev) =>
+ prev.map((n) => (n.id === notificationId ? { ...n, read: true } : n))
+ )
+ } catch (error) {
+ console.error('Failed to mark notification as read:', error)
+ }
+ }
+
+ const handleMarkAllAsRead = async () => {
+ try {
+ await notificationService.markAllAsRead()
+ setNotifications((prev) => prev.map((n) => ({ ...n, read: true })))
+ } catch (error) {
+ console.error('Failed to mark all as read:', error)
+ }
+ }
+
+ const handleClearAll = async () => {
+ if (!confirm('Clear all notifications?')) return
+ try {
+ await notificationService.clearAll()
+ setNotifications([])
+ } catch (error) {
+ console.error('Failed to clear notifications:', error)
+ }
+ }
+
+ const filteredNotifications =
+ filter === 'unread'
+ ? notifications.filter((n) => !n.read)
+ : notifications
+
+ const unreadCount = notifications.filter((n) => !n.read).length
+
+ if (!isOpen) return null
+
+ return (
+ <>
+ {/* Overlay */}
+
+
+ {/* Panel */}
+
+ {/* Header */}
+
+
+
+ Notifications {unreadCount > 0 && `(${unreadCount})`}
+
+
+
+
+ {/* Filters */}
+
+
+
+
+
+
+ {/* Actions */}
+ {notifications.length > 0 && (
+
+ {unreadCount > 0 && (
+
+ )}
+
+
+ )}
+
+ {/* Notifications List */}
+
+ {loading ? (
+
Loading...
+ ) : filteredNotifications.length === 0 ? (
+
+ {filter === 'unread' ? 'No unread notifications' : 'No notifications'}
+
+ ) : (
+
+ {filteredNotifications.map((notification) => (
+
!notification.read && handleMarkAsRead(notification.id)}
+ >
+
+ {/* Icon */}
+
+ {notification.type === 'friend_request' && '👥'}
+ {notification.type === 'message' && '💬'}
+ {notification.type === 'call' && '📞'}
+ {notification.type === 'server_invite' && '🎮'}
+ {notification.type === 'system' && 'ℹ️'}
+
+
+ {/* Content */}
+
+
+
+ {notification.title}
+
+ {!notification.read && (
+
+ )}
+
+
+ {notification.message}
+
+
+ {new Date(notification.created_at).toLocaleString()}
+
+
+
+
+ {/* Action Button */}
+ {notification.action_url && (
+
+ )}
+
+ ))}
+
+ )}
+
+
+ >
+ )
+}
diff --git a/packages/web/src/components/ProtectedRoute.tsx b/packages/web/src/components/ProtectedRoute.tsx
new file mode 100644
index 0000000..8d832bd
--- /dev/null
+++ b/packages/web/src/components/ProtectedRoute.tsx
@@ -0,0 +1,12 @@
+import { useAppSelector } from '@/hooks/useRedux'
+import { Navigate } from 'react-router-dom'
+
+export default function ProtectedRoute({ element }: { element: React.ReactNode }) {
+ const { isAuthenticated } = useAppSelector((state) => state.auth)
+
+ if (!isAuthenticated) {
+ return
+ }
+
+ return <>{element}>
+}
diff --git a/packages/web/src/components/SearchComponent.tsx b/packages/web/src/components/SearchComponent.tsx
new file mode 100644
index 0000000..cda8b02
--- /dev/null
+++ b/packages/web/src/components/SearchComponent.tsx
@@ -0,0 +1,228 @@
+import { useState, useEffect, useRef } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { userService, serverService, messageService } from '@/services/api'
+import type { User, Server, Message } from '@/types'
+
+interface SearchComponentProps {
+ onClose?: () => void
+}
+
+export default function SearchComponent({ onClose }: SearchComponentProps) {
+ const navigate = useNavigate()
+ const inputRef = useRef(null)
+ const [query, setQuery] = useState('')
+ const [activeTab, setActiveTab] = useState<'all' | 'users' | 'servers' | 'messages'>('all')
+ const [results, setResults] = useState<{
+ users: User[]
+ servers: Server[]
+ messages: Message[]
+ }>({
+ users: [],
+ servers: [],
+ messages: [],
+ })
+ const [loading, setLoading] = useState(false)
+
+ useEffect(() => {
+ inputRef.current?.focus()
+ }, [])
+
+ useEffect(() => {
+ if (!query.trim()) {
+ setResults({ users: [], servers: [], messages: [] })
+ return
+ }
+
+ const searchTimeout = setTimeout(() => {
+ performSearch()
+ }, 300) // Debounce search
+
+ return () => clearTimeout(searchTimeout)
+ }, [query, activeTab])
+
+ const performSearch = async () => {
+ setLoading(true)
+ try {
+ if (activeTab === 'all' || activeTab === 'users') {
+ const usersResp = await userService.search(query)
+ setResults((prev) => ({ ...prev, users: usersResp.data }))
+ }
+
+ if (activeTab === 'all' || activeTab === 'servers') {
+ const serversResp = await serverService.search(query)
+ setResults((prev) => ({ ...prev, servers: serversResp.data }))
+ }
+
+ if (activeTab === 'all' || activeTab === 'messages') {
+ const messagesResp = await messageService.search(query)
+ setResults((prev) => ({ ...prev, messages: messagesResp.data }))
+ }
+ } catch (error) {
+ console.error('Search failed:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleUserClick = (userId: string) => {
+ navigate(`/profile/${userId}`)
+ onClose?.()
+ }
+
+ const handleServerClick = (serverId: string) => {
+ navigate(`/chat?server=${serverId}`)
+ onClose?.()
+ }
+
+ const handleMessageClick = (message: Message) => {
+ navigate(`/chat?channel=${message.channel_id}&message=${message.id}`)
+ onClose?.()
+ }
+
+ const totalResults = results.users.length + results.servers.length + results.messages.length
+
+ return (
+
+
+ {/* Search Input */}
+
+ setQuery(e.target.value)}
+ placeholder="Search for users, servers, or messages..."
+ className="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded text-white placeholder-gray-400 focus:outline-none focus:border-purple-500"
+ />
+
+
+
+ {/* Tabs */}
+
+ {(['all', 'users', 'servers', 'messages'] as const).map((tab) => (
+
+ ))}
+
+
+ {/* Results */}
+
+ {loading ? (
+
Searching...
+ ) : !query.trim() ? (
+
+ Start typing to search...
+
+ ) : totalResults === 0 ? (
+
No results found
+ ) : (
+
+ {/* Users */}
+ {(activeTab === 'all' || activeTab === 'users') &&
+ results.users.length > 0 && (
+
+
+ Users
+
+
+ {results.users.map((user) => (
+
+ ))}
+
+
+ )}
+
+ {/* Servers */}
+ {(activeTab === 'all' || activeTab === 'servers') &&
+ results.servers.length > 0 && (
+
+
+ Servers
+
+
+ {results.servers.map((server) => (
+
+ ))}
+
+
+ )}
+
+ {/* Messages */}
+ {(activeTab === 'all' || activeTab === 'messages') &&
+ results.messages.length > 0 && (
+
+
+ Messages
+
+
+ {results.messages.map((message) => (
+
+ ))}
+
+
+ )}
+
+ )}
+
+
+ {/* Footer */}
+
+ Tip: Use Ctrl+K to open search
+
+
+
+ )
+}
diff --git a/packages/web/src/components/ServerList.tsx b/packages/web/src/components/ServerList.tsx
new file mode 100644
index 0000000..93d473b
--- /dev/null
+++ b/packages/web/src/components/ServerList.tsx
@@ -0,0 +1,51 @@
+import type { Server } from '@/types'
+import { Settings, Plus } from 'lucide-react'
+
+interface ServerListProps {
+ servers: Server[]
+ selectedServer: Server | null
+ onSelectServer: (server: Server) => void
+}
+
+export default function ServerList({
+ servers,
+ selectedServer,
+ onSelectServer,
+}: ServerListProps) {
+ return (
+
+ {/* Home */}
+
+
+ {/* Server icons */}
+
+ {servers.map((server) => (
+
+ ))}
+
+
+ {/* Add server button */}
+
+
+ {/* Settings */}
+
+
+ )
+}
diff --git a/packages/web/src/hooks/index.ts b/packages/web/src/hooks/index.ts
new file mode 100644
index 0000000..c193f2e
--- /dev/null
+++ b/packages/web/src/hooks/index.ts
@@ -0,0 +1,41 @@
+import { useEffect } from 'react'
+
+export const useLocalStorage = (key: string, initialValue: T): [T, (value: T) => void] => {
+ const [storedValue, setStoredValue] = React.useState(() => {
+ try {
+ const item = window.localStorage.getItem(key)
+ return item ? JSON.parse(item) : initialValue
+ } catch (error) {
+ console.log(error)
+ return initialValue
+ }
+ })
+
+ const setValue = (value: T) => {
+ try {
+ const valueToStore = value instanceof Function ? value(storedValue) : value
+ setStoredValue(valueToStore)
+ window.localStorage.setItem(key, JSON.stringify(valueToStore))
+ } catch (error) {
+ console.log(error)
+ }
+ }
+
+ return [storedValue, setValue]
+}
+
+import React from 'react'
+
+export const useDebounce = (value: T, delay: number): T => {
+ const [debouncedValue, setDebouncedValue] = React.useState(value)
+
+ useEffect(() => {
+ const handler = setTimeout(() => {
+ setDebouncedValue(value)
+ }, delay)
+
+ return () => clearTimeout(handler)
+ }, [value, delay])
+
+ return debouncedValue
+}
diff --git a/packages/web/src/hooks/use-store.ts b/packages/web/src/hooks/use-store.ts
new file mode 100644
index 0000000..d15f30c
--- /dev/null
+++ b/packages/web/src/hooks/use-store.ts
@@ -0,0 +1,19 @@
+import { useSelector, useDispatch } from 'react-redux'
+import { RootState, AppDispatch } from '@/stores'
+
+export const useAppDispatch = () => useDispatch()
+
+export const useAppSelector = (selector: (state: RootState) => T): T =>
+ useSelector(selector)
+
+export const useAuth = () => {
+ return useAppSelector((state) => state.auth)
+}
+
+export const useMessaging = () => {
+ return useAppSelector((state) => state.messaging)
+}
+
+export const useCall = () => {
+ return useAppSelector((state) => state.call)
+}
diff --git a/packages/web/src/hooks/useRedux.ts b/packages/web/src/hooks/useRedux.ts
new file mode 100644
index 0000000..70b56c5
--- /dev/null
+++ b/packages/web/src/hooks/useRedux.ts
@@ -0,0 +1,5 @@
+import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux'
+import type { RootState, AppDispatch } from '@/stores'
+
+export const useAppDispatch = () => useDispatch()
+export const useAppSelector: TypedUseSelectorHook = useSelector
diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx
new file mode 100644
index 0000000..448b230
--- /dev/null
+++ b/packages/web/src/main.tsx
@@ -0,0 +1,14 @@
+import '@/styles/globals.css'
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { Provider } from 'react-redux'
+import { store } from '@/stores'
+import App from '@/App'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+ ,
+)
diff --git a/packages/web/src/pages/Admin.tsx b/packages/web/src/pages/Admin.tsx
new file mode 100644
index 0000000..dd21f81
--- /dev/null
+++ b/packages/web/src/pages/Admin.tsx
@@ -0,0 +1,171 @@
+import { useEffect, useState } from 'react'
+import { useAppSelector } from '@/hooks/useRedux'
+import { serverService, userService } from '@/services/api'
+import type { Server, User } from '@/types'
+
+export default function AdminPage() {
+ const { user } = useAppSelector((state) => state.auth)
+ const [activeTab, setActiveTab] = useState<'servers' | 'users' | 'roles'>('servers')
+ const [servers, setServers] = useState([])
+ const [users, setUsers] = useState([])
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ loadData()
+ }, [activeTab])
+
+ const loadData = async () => {
+ setLoading(true)
+ try {
+ if (activeTab === 'servers') {
+ const response = await serverService.list()
+ setServers(response.data)
+ } else if (activeTab === 'users') {
+ const response = await userService.list()
+ setUsers(response.data)
+ }
+ } catch (error) {
+ console.error('Failed to load admin data:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleDeleteServer = async (serverId: string) => {
+ if (!confirm('Are you sure you want to delete this server?')) return
+ try {
+ await serverService.delete(serverId)
+ setServers(servers.filter((s) => s.id !== serverId))
+ } catch (error) {
+ console.error('Failed to delete server:', error)
+ alert('Failed to delete server')
+ }
+ }
+
+ const handleBanUser = async (userId: string) => {
+ if (!confirm('Are you sure you want to ban this user?')) return
+ try {
+ await userService.ban(userId)
+ setUsers(users.map((u) => (u.id === userId ? { ...u, banned: true } : u)))
+ } catch (error) {
+ console.error('Failed to ban user:', error)
+ alert('Failed to ban user')
+ }
+ }
+
+ if (!user || user.role !== 'admin') {
+ return (
+
+
+
Access Denied
+
You need admin privileges to access this page.
+
+
+ )
+ }
+
+ return (
+
+
Admin Dashboard
+
+ {/* Tabs */}
+
+ {(['servers', 'users', 'roles'] as const).map((tab) => (
+
+ ))}
+
+
+ {loading ? (
+
Loading...
+ ) : (
+
+ {activeTab === 'servers' && (
+
+
Manage Servers
+
+ {servers.length === 0 ? (
+
No servers found
+ ) : (
+ servers.map((server) => (
+
+
+
{server.name}
+
+ Owner: {server.owner_id} | Members: {server.member_count || 0}
+
+
+
+
+ ))
+ )}
+
+
+ )}
+
+ {activeTab === 'users' && (
+
+
Manage Users
+
+ {users.length === 0 ? (
+
No users found
+ ) : (
+ users.map((userItem) => (
+
+
+
+ {userItem.username || userItem.email}
+
+
+ Email: {userItem.email}
+ {userItem.banned && (
+ BANNED
+ )}
+
+
+ {!userItem.banned && (
+
+ )}
+
+ ))
+ )}
+
+
+ )}
+
+ {activeTab === 'roles' && (
+
+
Role Management
+
Role management UI coming soon...
+
+ )}
+
+ )}
+
+ )
+}
diff --git a/packages/web/src/pages/Calls.tsx b/packages/web/src/pages/Calls.tsx
new file mode 100644
index 0000000..3a08844
--- /dev/null
+++ b/packages/web/src/pages/Calls.tsx
@@ -0,0 +1,138 @@
+import { useState } from 'react'
+import { useAppSelector } from '@/hooks/useRedux'
+import { callService } from '@/services/api'
+import type { User } from '@/types'
+
+export default function CallsPage() {
+ const { user } = useAppSelector((state) => state.auth)
+ const [contacts] = useState([])
+ const [activeCall, setActiveCall] = useState(null)
+ const [incomingCall, setIncomingCall] = useState(null)
+
+ const handleInitiateCall = async (targetUserId: string, type: 'voice' | 'video') => {
+ try {
+ const response = await callService.initiate(targetUserId, type)
+ setActiveCall(response.data)
+ } catch (error) {
+ console.error('Failed to initiate call:', error)
+ }
+ }
+
+ const handleAcceptCall = async () => {
+ if (!incomingCall) return
+ try {
+ await callService.accept(incomingCall.id)
+ setActiveCall(incomingCall)
+ setIncomingCall(null)
+ } catch (error) {
+ console.error('Failed to accept call:', error)
+ }
+ }
+
+ const handleDeclineCall = async () => {
+ if (!incomingCall) return
+ try {
+ await callService.decline(incomingCall.id)
+ setIncomingCall(null)
+ } catch (error) {
+ console.error('Failed to decline call:', error)
+ }
+ }
+
+ const handleEndCall = async () => {
+ if (!activeCall) return
+ try {
+ await callService.end(activeCall.id)
+ setActiveCall(null)
+ } catch (error) {
+ console.error('Failed to end call:', error)
+ }
+ }
+
+ if (!user) {
+ return (
+
+ )
+ }
+
+ return (
+
+
Voice & Video Calls
+
+ {/* Incoming Call Alert */}
+ {incomingCall && (
+
+
+ Incoming call from {incomingCall.caller_name}
+
+
+
+
+
+
+ )}
+
+ {/* Active Call */}
+ {activeCall && (
+
+
+ In call with {activeCall.target_name}
+
+
+
+ )}
+
+ {/* Contacts List */}
+
+ {contacts.length === 0 ? (
+
No contacts available
+ ) : (
+ contacts.map((contact) => (
+
+

+
{contact.display_name}
+
@{contact.username}
+
+
+
+
+
+ ))
+ )}
+
+
+ )
+}
diff --git a/packages/web/src/pages/Chat.tsx b/packages/web/src/pages/Chat.tsx
new file mode 100644
index 0000000..57f0d25
--- /dev/null
+++ b/packages/web/src/pages/Chat.tsx
@@ -0,0 +1,114 @@
+import { useEffect, useState } from 'react'
+import { useAppSelector, useAppDispatch } from '@/hooks/useRedux'
+import { setCurrentChannel, messagesLoaded } from '@/stores/messagingSlice'
+import { messageService, serverService } from '@/services/api'
+import type { Server, Channel } from '@/types'
+import ServerList from '@/components/ServerList'
+import ChannelList from '@/components/ChannelList'
+import ChatArea from '@/components/ChatArea'
+import MemberList from '@/components/MemberList'
+
+export default function ChatPage() {
+ const dispatch = useAppDispatch()
+ const { user } = useAppSelector((state) => state.auth)
+ const { activeChannelId, messages } = useAppSelector((state) => state.messaging)
+ const [servers, setServers] = useState([])
+ const [channels, setChannels] = useState([])
+ const [selectedServer, setSelectedServer] = useState(null)
+ const [loading, setLoading] = useState(true)
+
+ // Load servers on mount
+ useEffect(() => {
+ const loadServers = async () => {
+ try {
+ const response = await serverService.list()
+ setServers(response.data)
+ if (response.data.length > 0) {
+ setSelectedServer(response.data[0])
+ }
+ } catch (error) {
+ console.error('Failed to load servers:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ loadServers()
+ }, [])
+
+ // Load channels when server changes
+ useEffect(() => {
+ if (!selectedServer) return
+
+ const loadChannels = async () => {
+ try {
+ await serverService.get(selectedServer.id)
+ // Note: channels would be part of the response
+ setChannels([])
+ } catch (error) {
+ console.error('Failed to load channels:', error)
+ }
+ }
+
+ loadChannels()
+ }, [selectedServer])
+
+ // Load messages when channel changes
+ useEffect(() => {
+ if (!activeChannelId) return
+
+ const loadMessages = async () => {
+ try {
+ const messagesData = await messageService.list(activeChannelId)
+ dispatch(messagesLoaded(messagesData.data))
+ } catch (error) {
+ console.error('Failed to load messages:', error)
+ }
+ }
+
+ loadMessages()
+ }, [activeChannelId, dispatch])
+
+ if (!user) {
+ return (
+
+ )
+ }
+
+ if (loading) {
+ return (
+
+ )
+ }
+
+ return (
+
+ {/* Server List */}
+
+
+ {/* Channel List */}
+
dispatch(setCurrentChannel(channelId))}
+ serverName={selectedServer?.name || 'AeThex'}
+ />
+
+ {/* Chat Area */}
+
+
+
+
+ {/* Member List */}
+ {selectedServer && }
+
+ )
+}
diff --git a/packages/web/src/pages/Friends.tsx b/packages/web/src/pages/Friends.tsx
new file mode 100644
index 0000000..7623981
--- /dev/null
+++ b/packages/web/src/pages/Friends.tsx
@@ -0,0 +1,342 @@
+import { useEffect, useState } from 'react'
+import { useAppSelector } from '@/hooks/useRedux'
+import { userService } from '@/services/api'
+import type { User } from '@/types'
+
+interface FriendRequest {
+ id: string
+ from_user: User
+ to_user: User
+ status: 'pending' | 'accepted' | 'declined'
+ created_at: string
+}
+
+export default function FriendsPage() {
+ const { user } = useAppSelector((state) => state.auth)
+ const [activeTab, setActiveTab] = useState<'all' | 'pending' | 'blocked'>('all')
+ const [friends, setFriends] = useState([])
+ const [pendingRequests, setPendingRequests] = useState([])
+ const [blockedUsers, setBlockedUsers] = useState([])
+ const [searchQuery, setSearchQuery] = useState('')
+ const [searchResults, setSearchResults] = useState([])
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ loadFriends()
+ }, [])
+
+ const loadFriends = async () => {
+ setLoading(true)
+ try {
+ const [friendsResp, pendingResp, blockedResp] = await Promise.all([
+ userService.getFriends(),
+ userService.getFriendRequests(),
+ userService.getBlockedUsers(),
+ ])
+ setFriends(friendsResp.data)
+ setPendingRequests(pendingResp.data)
+ setBlockedUsers(blockedResp.data)
+ } catch (error) {
+ console.error('Failed to load friends:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleSearch = async () => {
+ if (!searchQuery.trim()) {
+ setSearchResults([])
+ return
+ }
+ try {
+ const response = await userService.search(searchQuery)
+ setSearchResults(response.data)
+ } catch (error) {
+ console.error('Failed to search users:', error)
+ }
+ }
+
+ const handleSendFriendRequest = async (targetUserId: string) => {
+ try {
+ await userService.sendFriendRequest(targetUserId)
+ alert('Friend request sent!')
+ setSearchResults([])
+ setSearchQuery('')
+ } catch (error) {
+ console.error('Failed to send friend request:', error)
+ alert('Failed to send friend request')
+ }
+ }
+
+ const handleAcceptRequest = async (requestId: string) => {
+ try {
+ await userService.acceptFriendRequest(requestId)
+ await loadFriends()
+ } catch (error) {
+ console.error('Failed to accept request:', error)
+ }
+ }
+
+ const handleDeclineRequest = async (requestId: string) => {
+ try {
+ await userService.declineFriendRequest(requestId)
+ setPendingRequests(pendingRequests.filter((r) => r.id !== requestId))
+ } catch (error) {
+ console.error('Failed to decline request:', error)
+ }
+ }
+
+ const handleRemoveFriend = async (friendId: string) => {
+ if (!confirm('Remove this friend?')) return
+ try {
+ await userService.removeFriend(friendId)
+ setFriends(friends.filter((f) => f.id !== friendId))
+ } catch (error) {
+ console.error('Failed to remove friend:', error)
+ }
+ }
+
+ const handleBlockUser = async (targetUserId: string) => {
+ if (!confirm('Block this user?')) return
+ try {
+ await userService.blockUser(targetUserId)
+ await loadFriends()
+ } catch (error) {
+ console.error('Failed to block user:', error)
+ }
+ }
+
+ const handleUnblockUser = async (targetUserId: string) => {
+ try {
+ await userService.unblockUser(targetUserId)
+ setBlockedUsers(blockedUsers.filter((u) => u.id !== targetUserId))
+ } catch (error) {
+ console.error('Failed to unblock user:', error)
+ }
+ }
+
+ if (!user) {
+ return (
+
+ )
+ }
+
+ return (
+
+
Friends
+
+ {/* Search Bar */}
+
+
+ setSearchQuery(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
+ placeholder="Search users by username or email..."
+ className="flex-1 px-4 py-2 bg-gray-800 border border-gray-700 rounded text-white placeholder-gray-400"
+ />
+
+
+
+ {/* Search Results */}
+ {searchResults.length > 0 && (
+
+ {searchResults.map((result) => (
+
+
+

+
+
{result.username}
+
{result.email}
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Tabs */}
+
+ {(['all', 'pending', 'blocked'] as const).map((tab) => (
+
+ ))}
+
+
+ {loading ? (
+
Loading...
+ ) : (
+
+ {activeTab === 'all' && (
+ <>
+ {friends.length === 0 ? (
+
No friends yet. Search for users to add!
+ ) : (
+ friends.map((friend) => (
+
+
+
+

+
+
+
+
+ {friend.display_name || friend.username}
+
+
@{friend.username}
+ {friend.custom_status && (
+
{friend.custom_status}
+ )}
+
+
+
+
+
+
+
+ ))
+ )}
+ >
+ )}
+
+ {activeTab === 'pending' && (
+ <>
+ {pendingRequests.length === 0 ? (
+
No pending friend requests
+ ) : (
+ pendingRequests.map((request) => (
+
+
+

+
+
+ {request.from_user.username}
+
+
+ Sent {new Date(request.created_at).toLocaleDateString()}
+
+
+
+
+
+
+
+
+ ))
+ )}
+ >
+ )}
+
+ {activeTab === 'blocked' && (
+ <>
+ {blockedUsers.length === 0 ? (
+
No blocked users
+ ) : (
+ blockedUsers.map((blockedUser) => (
+
+
+

+
+
+ {blockedUser.username}
+
+
Blocked
+
+
+
+
+ ))
+ )}
+ >
+ )}
+
+ )}
+
+ )
+}
diff --git a/packages/web/src/pages/Login.tsx b/packages/web/src/pages/Login.tsx
new file mode 100644
index 0000000..05e5b82
--- /dev/null
+++ b/packages/web/src/pages/Login.tsx
@@ -0,0 +1,93 @@
+import { useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { useAppDispatch } from '@/hooks/useRedux'
+import { loginStart, loginSuccess, loginFailure } from '@/stores/authSlice'
+import { authService } from '@/services/api'
+
+export default function LoginPage() {
+ const navigate = useNavigate()
+ const dispatch = useAppDispatch()
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const [error, setError] = useState('')
+ const [isLoading, setIsLoading] = useState(false)
+
+ const handleLogin = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError('')
+ setIsLoading(true)
+ dispatch(loginStart())
+
+ try {
+ const response = await authService.login(email, password)
+ dispatch(loginSuccess(response.data))
+ navigate('/app')
+ } catch (err: any) {
+ const errorMsg = err.response?.data?.message || 'Login failed'
+ setError(errorMsg)
+ dispatch(loginFailure(errorMsg))
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+
+
+
AeThex Connect
+
Gaming Communication Platform
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+ Don't have an account?{' '}
+
+ Sign up
+
+
+
+
+ )
+}
diff --git a/packages/web/src/pages/Profile.tsx b/packages/web/src/pages/Profile.tsx
new file mode 100644
index 0000000..31b7b00
--- /dev/null
+++ b/packages/web/src/pages/Profile.tsx
@@ -0,0 +1,210 @@
+import { useEffect, useState } from 'react'
+import { useParams } from 'react-router-dom'
+import { useAppSelector } from '@/hooks/useRedux'
+import { userService } from '@/services/api'
+import type { User } from '@/types'
+
+export default function ProfilePage() {
+ const { userId } = useParams<{ userId: string }>()
+ const { user: currentUser } = useAppSelector((state) => state.auth)
+ const [profile, setProfile] = useState(null)
+ const [loading, setLoading] = useState(true)
+ const [isEditing, setIsEditing] = useState(false)
+ const [editForm, setEditForm] = useState({
+ display_name: '',
+ custom_status: '',
+ avatar_url: '',
+ })
+
+ useEffect(() => {
+ loadProfile()
+ }, [userId])
+
+ const loadProfile = async () => {
+ setLoading(true)
+ try {
+ const targetId = userId || currentUser?.id
+ if (!targetId) return
+
+ const response = await userService.get(targetId)
+ setProfile(response.data)
+ setEditForm({
+ display_name: response.data.display_name || '',
+ custom_status: response.data.custom_status || '',
+ avatar_url: response.data.avatar_url || '',
+ })
+ } catch (error) {
+ console.error('Failed to load profile:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleSave = async () => {
+ if (!profile) return
+ try {
+ const response = await userService.updateProfile(profile.id, editForm)
+ setProfile(response.data)
+ setIsEditing(false)
+ } catch (error) {
+ console.error('Failed to update profile:', error)
+ alert('Failed to update profile')
+ }
+ }
+
+ const isOwnProfile = !userId || userId === currentUser?.id
+
+ if (loading) {
+ return (
+
+ )
+ }
+
+ if (!profile) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ {/* Profile Header */}
+
+
+ {/* Avatar */}
+
+

+
+
+
+ {/* User Info */}
+
+
+ {profile.display_name || profile.username || 'Unknown User'}
+
+
@{profile.username}
+ {profile.custom_status && (
+
{profile.custom_status}
+ )}
+ {profile.verified_domain && (
+
✓ {profile.verified_domain}
+ )}
+
+
+ {/* Edit Button */}
+ {isOwnProfile && (
+
+ )}
+
+
+
+ {/* Edit Form */}
+ {isEditing && (
+
+ )}
+
+ {/* Profile Stats */}
+
+
+
+
+
+ {profile.premium_tier || 'free'}
+
+
Tier
+
+
+
+ {/* Activity Section */}
+
+
Recent Activity
+
No recent activity
+
+
+
+ )
+}
diff --git a/packages/web/src/pages/Settings.tsx b/packages/web/src/pages/Settings.tsx
new file mode 100644
index 0000000..4fc0ecb
--- /dev/null
+++ b/packages/web/src/pages/Settings.tsx
@@ -0,0 +1,148 @@
+import { useState, useEffect } from 'react'
+import { useAppSelector, useAppDispatch } from '@/hooks/useRedux'
+import { setUser } from '@/stores/authSlice'
+import { userService } from '@/services/api'
+import type { User } from '@/types'
+
+export default function SettingsPage() {
+ const dispatch = useAppDispatch()
+ const { user } = useAppSelector((state) => state.auth)
+ const [profile, setProfile] = useState>({})
+ const [activeTab, setActiveTab] = useState<'profile' | 'appearance' | 'privacy'>('profile')
+ const [isSaving, setIsSaving] = useState(false)
+ const [message, setMessage] = useState('')
+
+ useEffect(() => {
+ if (user) {
+ setProfile(user)
+ }
+ }, [user])
+
+ const handleSaveProfile = async () => {
+ if (!user) return
+ setIsSaving(true)
+ try {
+ const response = await userService.updateProfile(user.id, profile)
+ dispatch(setUser(response.data))
+ setMessage('Profile updated successfully')
+ setTimeout(() => setMessage(''), 3000)
+ } catch (error) {
+ setMessage('Failed to update profile')
+ } finally {
+ setIsSaving(false)
+ }
+ }
+
+ if (!user) {
+ return (
+
+ )
+ }
+
+ return (
+
+
Settings
+
+ {message && (
+
+ {message}
+
+ )}
+
+
+ {/* Sidebar */}
+
+
+
+
+ {/* Content */}
+
+ {activeTab === 'profile' && (
+
+
Profile Settings
+
+
+
+
+ setProfile({ ...profile, display_name: e.target.value })
+ }
+ className="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded text-white"
+ />
+
+
+
+
+ setProfile({ ...profile, username: e.target.value })}
+ className="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded text-white"
+ />
+
+
+
+
+
+ setProfile({ ...profile, custom_status: e.target.value })
+ }
+ className="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded text-white"
+ placeholder="What's your status?"
+ />
+
+
+
+
+ )}
+
+ {activeTab === 'appearance' && (
+
+
Appearance
+
Theme settings coming soon...
+
+ )}
+
+ {activeTab === 'privacy' && (
+
+
Privacy & Safety
+
Privacy settings coming soon...
+
+ )}
+
+
+
+ )
+}
diff --git a/packages/web/src/pages/Signup.tsx b/packages/web/src/pages/Signup.tsx
new file mode 100644
index 0000000..30bb005
--- /dev/null
+++ b/packages/web/src/pages/Signup.tsx
@@ -0,0 +1,107 @@
+import { useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { useAppDispatch } from '@/hooks/useRedux'
+import { loginSuccess, loginFailure } from '@/stores/authSlice'
+import { authService } from '@/services/api'
+
+export default function SignupPage() {
+ const navigate = useNavigate()
+ const dispatch = useAppDispatch()
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const [username, setUsername] = useState('')
+ const [error, setError] = useState('')
+ const [isLoading, setIsLoading] = useState(false)
+
+ const handleSignup = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError('')
+ setIsLoading(true)
+
+ try {
+ const response = await authService.signup(email, password, username)
+ dispatch(loginSuccess(response.data))
+ navigate('/app')
+ } catch (err: any) {
+ const errorMsg = err.response?.data?.message || 'Signup failed'
+ setError(errorMsg)
+ dispatch(loginFailure(errorMsg))
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+
+
+
Create Account
+
Join AeThex Connect today
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+ Already have an account?{' '}
+
+ Login
+
+
+
+
+ )
+}
diff --git a/packages/web/src/services/api.ts b/packages/web/src/services/api.ts
new file mode 100644
index 0000000..80b05b5
--- /dev/null
+++ b/packages/web/src/services/api.ts
@@ -0,0 +1,274 @@
+import axios, { AxiosError, AxiosInstance } from 'axios'
+
+// Type definition for Vite import.meta.env
+declare global {
+ interface ImportMetaEnv {
+ readonly VITE_API_URL?: string
+ }
+
+ interface ImportMeta {
+ readonly env: ImportMetaEnv
+ }
+}
+
+const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'
+
+class ApiClient {
+ private client: AxiosInstance
+
+ constructor() {
+ this.client = axios.create({
+ baseURL: API_BASE_URL,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+
+ // Add token to requests if available
+ this.client.interceptors.request.use((config) => {
+ const token = localStorage.getItem('auth_token')
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+ return config
+ })
+
+ // Handle errors
+ this.client.interceptors.response.use(
+ (response) => response,
+ (error: AxiosError) => {
+ if (error.response?.status === 401) {
+ localStorage.removeItem('auth_token')
+ window.location.href = '/login'
+ }
+ return Promise.reject(error)
+ }
+ )
+ }
+
+ // Auth APIs
+ async signUp(email: string, password: string, username: string) {
+ return this.client.post('/api/auth/signup', { email, password, username })
+ }
+
+ async login(email: string, password: string) {
+ return this.client.post('/api/auth/login', { email, password })
+ }
+
+ async logout() {
+ return this.client.post('/api/auth/logout')
+ }
+
+ async getMe() {
+ return this.client.get('/api/auth/me')
+ }
+
+ // Messaging APIs
+ async getServers() {
+ return this.client.get('/api/servers')
+ }
+
+ async createServer(name: string, description?: string) {
+ return this.client.post('/api/servers', { name, description })
+ }
+
+ async getChannels(serverId: string) {
+ return this.client.get(`/api/servers/${serverId}/channels`)
+ }
+
+ async createChannel(serverId: string, name: string, type: 'text' | 'voice' = 'text') {
+ return this.client.post(`/api/servers/${serverId}/channels`, { name, type })
+ }
+
+ async getMessages(channelId: string, limit: number = 50) {
+ return this.client.get(`/api/channels/${channelId}/messages?limit=${limit}`)
+ }
+
+ async sendMessage(channelId: string, content: string) {
+ return this.client.post(`/api/channels/${channelId}/messages`, { content })
+ }
+
+ // Call APIs
+ async initiateCall(recipientId: string, callType: 'voice' | 'video') {
+ return this.client.post('/api/calls', { recipientId, callType })
+ }
+
+ async endCall(callId: string) {
+ return this.client.post(`/api/calls/${callId}/end`)
+ }
+
+ // User APIs
+ async getUser(userId: string) {
+ return this.client.get(`/api/users/${userId}`)
+ }
+
+ async updateProfile(data: any) {
+ return this.client.patch('/api/users/profile', data)
+ }
+
+ // Relationships APIs
+ async sendFriendRequest(targetUserId: string) {
+ return this.client.post('/api/relationships/friend-request', { targetUserId })
+ }
+
+ async getFriendRequests() {
+ return this.client.get('/api/relationships/friend-requests')
+ }
+
+ async acceptFriendRequest(relationshipId: string) {
+ return this.client.post(`/api/relationships/${relationshipId}/accept`)
+ }
+
+ async rejectFriendRequest(relationshipId: string) {
+ return this.client.post(`/api/relationships/${relationshipId}/reject`)
+ }
+
+ // Premium APIs
+ async getPremiumStatus() {
+ return this.client.get('/api/premium/status')
+ }
+
+ async upgradePremium(tier: 'premium' | 'enterprise') {
+ return this.client.post('/api/premium/upgrade', { tier })
+ }
+
+ // Notification APIs
+ async getNotifications() {
+ return this.client.get('/api/notifications')
+ }
+
+ async markNotificationAsRead(notificationId: string) {
+ return this.client.patch(`/api/notifications/${notificationId}/read`)
+ }
+
+ async markAllNotificationsAsRead() {
+ return this.client.post('/api/notifications/mark-all-read')
+ }
+
+ async clearAllNotifications() {
+ return this.client.delete('/api/notifications/clear-all')
+ }
+
+ // Search APIs
+ async searchUsers(query: string) {
+ return this.client.get(`/api/search/users?q=${encodeURIComponent(query)}`)
+ }
+
+ async searchServers(query: string) {
+ return this.client.get(`/api/search/servers?q=${encodeURIComponent(query)}`)
+ }
+
+ async searchMessages(query: string) {
+ return this.client.get(`/api/search/messages?q=${encodeURIComponent(query)}`)
+ }
+
+ // Admin APIs
+ async listAllUsers() {
+ return this.client.get('/api/admin/users')
+ }
+
+ async listAllServers() {
+ return this.client.get('/api/admin/servers')
+ }
+
+ async deleteServer(serverId: string) {
+ return this.client.delete(`/api/admin/servers/${serverId}`)
+ }
+
+ async banUser(userId: string) {
+ return this.client.post(`/api/admin/users/${userId}/ban`)
+ }
+
+ async unbanUser(userId: string) {
+ return this.client.post(`/api/admin/users/${userId}/unban`)
+ }
+
+ // Friends/Blocking APIs
+ async getFriends() {
+ return this.client.get('/api/relationships/friends')
+ }
+
+ async getBlockedUsers() {
+ return this.client.get('/api/relationships/blocked')
+ }
+
+ async blockUser(userId: string) {
+ return this.client.post('/api/relationships/block', { userId })
+ }
+
+ async unblockUser(userId: string) {
+ return this.client.post('/api/relationships/unblock', { userId })
+ }
+
+ async removeFriend(userId: string) {
+ return this.client.delete(`/api/relationships/friends/${userId}`)
+ }
+}
+
+export const apiClient = new ApiClient()
+
+// Service exports for easy importing
+export const authService = {
+ signup: (email: string, password: string, username: string) =>
+ apiClient.signUp(email, password, username),
+ login: (email: string, password: string) => apiClient.login(email, password),
+ logout: () => apiClient.logout(),
+ getMe: () => apiClient.getMe(),
+}
+
+export const serverService = {
+ list: () => apiClient.getServers(),
+ get: async (id: string) => {
+ try {
+ return await axios.get(`${API_BASE_URL}/api/servers/${id}`)
+ } catch {
+ return { data: { id, name: 'Unknown Server', channels: [] } }
+ }
+ },
+ create: (name: string, description?: string) =>
+ apiClient.createServer(name, description),
+ delete: (id: string) => apiClient.deleteServer(id),
+ search: (query: string) => apiClient.searchServers(query),
+}
+
+export const messageService = {
+ list: (channelId: string, limit?: number) => apiClient.getMessages(channelId, limit),
+ send: (channelId: string, content: string) =>
+ apiClient.sendMessage(channelId, content),
+ search: (query: string) => apiClient.searchMessages(query),
+}
+
+export const callService = {
+ initiate: (recipientId: string, callType: 'voice' | 'video') =>
+ apiClient.initiateCall(recipientId, callType),
+ accept: (callId: string) => axios.post(`${API_BASE_URL}/api/calls/${callId}/accept`),
+ decline: (callId: string) => axios.post(`${API_BASE_URL}/api/calls/${callId}/decline`),
+ end: (callId: string) => apiClient.endCall(callId),
+}
+
+export const userService = {
+ get: (userId: string) => apiClient.getUser(userId),
+ list: () => apiClient.listAllUsers(),
+ updateProfile: (_userId: string, data: any) => apiClient.updateProfile(data),
+ search: (query: string) => apiClient.searchUsers(query),
+ getFriends: () => apiClient.getFriends(),
+ getFriendRequests: () => apiClient.getFriendRequests(),
+ sendFriendRequest: (userId: string) => apiClient.sendFriendRequest(userId),
+ acceptFriendRequest: (requestId: string) => apiClient.acceptFriendRequest(requestId),
+ declineFriendRequest: (requestId: string) =>
+ apiClient.rejectFriendRequest(requestId),
+ removeFriend: (userId: string) => apiClient.removeFriend(userId),
+ getBlockedUsers: () => apiClient.getBlockedUsers(),
+ blockUser: (userId: string) => apiClient.blockUser(userId),
+ unblockUser: (userId: string) => apiClient.unblockUser(userId),
+ ban: (userId: string) => apiClient.banUser(userId),
+ unban: (userId: string) => apiClient.unbanUser(userId),
+}
+
+export const notificationService = {
+ list: () => apiClient.getNotifications(),
+ markAsRead: (notificationId: string) =>
+ apiClient.markNotificationAsRead(notificationId),
+ markAllAsRead: () => apiClient.markAllNotificationsAsRead(),
+ clearAll: () => apiClient.clearAllNotifications(),
+}
diff --git a/packages/web/src/services/socket.ts b/packages/web/src/services/socket.ts
new file mode 100644
index 0000000..acf87e7
--- /dev/null
+++ b/packages/web/src/services/socket.ts
@@ -0,0 +1,96 @@
+import { io, Socket } from 'socket.io-client'
+import { store } from '@/stores'
+import { addMessage, setMessages } from '@/stores/messagingSlice'
+
+// Type definition for Vite import.meta.env
+declare global {
+ interface ImportMetaEnv {
+ readonly VITE_API_URL?: string
+ }
+
+ interface ImportMeta {
+ readonly env: ImportMetaEnv
+ }
+}
+
+const SOCKET_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'
+
+class SocketService {
+ private socket: Socket | null = null
+
+ connect() {
+ const token = localStorage.getItem('auth_token')
+ if (!token) return
+
+ this.socket = io(SOCKET_URL, {
+ auth: { token },
+ transports: ['websocket', 'polling'],
+ })
+
+ this.setupListeners()
+ }
+
+ private setupListeners() {
+ if (!this.socket) return
+
+ // Connection events
+ this.socket.on('connect', () => {
+ console.log('Socket connected')
+ })
+
+ this.socket.on('disconnect', () => {
+ console.log('Socket disconnected')
+ })
+
+ // Messaging events
+ this.socket.on('message_received', (message) => {
+ store.dispatch(addMessage(message))
+ })
+
+ this.socket.on('messages_loaded', (messages) => {
+ store.dispatch(setMessages(messages))
+ })
+
+ this.socket.on('typing_indicator', () => {
+ // Handle typing indicator
+ })
+
+ // Call events
+ this.socket.on('incoming_call', (data) => {
+ console.log('Incoming call:', data)
+ })
+
+ this.socket.on('call_ended', (data) => {
+ console.log('Call ended:', data)
+ })
+ }
+
+ disconnect() {
+ if (this.socket) {
+ this.socket.disconnect()
+ }
+ }
+
+ // Emitters
+ joinChannel(channelId: string) {
+ this.socket?.emit('join_channel', { channelId })
+ }
+
+ sendMessage(channelId: string, content: string) {
+ this.socket?.emit('send_message', { channelId, content })
+ }
+
+ initiateCall(recipientId: string, callType: 'voice' | 'video') {
+ this.socket?.emit('call', { recipientId, callType })
+ }
+
+ endCall(callId: string) {
+ this.socket?.emit('end_call', { callId })
+ }
+
+ setTyping(channelId: string, isTyping: boolean) {
+ this.socket?.emit('typing', { channelId, isTyping })
+ }
+}
+
+export const socketService = new SocketService()
diff --git a/packages/web/src/stores/authSlice.ts b/packages/web/src/stores/authSlice.ts
new file mode 100644
index 0000000..0e801f5
--- /dev/null
+++ b/packages/web/src/stores/authSlice.ts
@@ -0,0 +1,70 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+import type { User } from '@/types'
+
+interface AuthState {
+ user: User | null
+ token: string | null
+ loading: boolean
+ isLoading: boolean
+ error: string | null
+ isAuthenticated: boolean
+}
+
+const initialState: AuthState = {
+ user: null,
+ token: localStorage.getItem('auth_token'),
+ loading: false,
+ isLoading: false,
+ error: null,
+ isAuthenticated: !!localStorage.getItem('auth_token'),
+}
+
+const authSlice = createSlice({
+ name: 'auth',
+ initialState,
+ reducers: {
+ loginStart: (state) => {
+ state.loading = true
+ state.isLoading = true
+ state.error = null
+ },
+ loginSuccess: (state, action: PayloadAction) => {
+ state.user = action.payload.user
+ state.token = action.payload.token
+ state.isAuthenticated = true
+ state.loading = false
+ state.isLoading = false
+ if (action.payload.token) {
+ localStorage.setItem('auth_token', action.payload.token)
+ }
+ },
+ loginFailure: (state, action: PayloadAction) => {
+ state.loading = false
+ state.isLoading = false
+ state.error = action.payload
+ },
+ setLoading: (state, action: PayloadAction) => {
+ state.loading = action.payload
+ },
+ setUser: (state, action: PayloadAction) => {
+ state.user = action.payload
+ state.isAuthenticated = true
+ },
+ setToken: (state, action: PayloadAction) => {
+ state.token = action.payload
+ localStorage.setItem('auth_token', action.payload)
+ },
+ setError: (state, action: PayloadAction) => {
+ state.error = action.payload
+ },
+ logout: (state) => {
+ state.user = null
+ state.token = null
+ state.isAuthenticated = false
+ localStorage.removeItem('auth_token')
+ },
+ },
+})
+
+export const { loginStart, loginSuccess, loginFailure, setLoading, setUser, setToken, setError, logout } = authSlice.actions
+export default authSlice.reducer
diff --git a/packages/web/src/stores/callSlice.ts b/packages/web/src/stores/callSlice.ts
new file mode 100644
index 0000000..720ca1d
--- /dev/null
+++ b/packages/web/src/stores/callSlice.ts
@@ -0,0 +1,69 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+
+interface CallState {
+ inCall: boolean
+ callId: string | null
+ callType: 'voice' | 'video' | null
+ remoteUserId: string | null
+ micEnabled: boolean
+ videoEnabled: boolean
+ screenShareEnabled: boolean
+ loading: boolean
+ error: string | null
+}
+
+const initialState: CallState = {
+ inCall: false,
+ callId: null,
+ callType: null,
+ remoteUserId: null,
+ micEnabled: true,
+ videoEnabled: true,
+ screenShareEnabled: false,
+ loading: false,
+ error: null,
+}
+
+const callSlice = createSlice({
+ name: 'call',
+ initialState,
+ reducers: {
+ startCall: (state, action: PayloadAction<{ callType: 'voice' | 'video'; remoteUserId: string }>) => {
+ state.inCall = true
+ state.callType = action.payload.callType
+ state.remoteUserId = action.payload.remoteUserId
+ state.loading = true
+ },
+ callConnected: (state, action: PayloadAction) => {
+ state.callId = action.payload
+ state.loading = false
+ },
+ endCall: (state) => {
+ state.inCall = false
+ state.callId = null
+ state.callType = null
+ state.remoteUserId = null
+ state.micEnabled = true
+ state.videoEnabled = true
+ state.screenShareEnabled = false
+ },
+ toggleMic: (state) => {
+ state.micEnabled = !state.micEnabled
+ },
+ toggleVideo: (state) => {
+ state.videoEnabled = !state.videoEnabled
+ },
+ toggleScreenShare: (state) => {
+ state.screenShareEnabled = !state.screenShareEnabled
+ },
+ setError: (state, action: PayloadAction) => {
+ state.error = action.payload
+ state.loading = false
+ },
+ },
+})
+
+export const { startCall, callConnected, endCall, toggleMic, toggleVideo, toggleScreenShare, setError } =
+ callSlice.actions
+
+export default callSlice.reducer
diff --git a/packages/web/src/stores/index.ts b/packages/web/src/stores/index.ts
new file mode 100644
index 0000000..18b64b6
--- /dev/null
+++ b/packages/web/src/stores/index.ts
@@ -0,0 +1,15 @@
+import { configureStore } from '@reduxjs/toolkit'
+import authReducer from './authSlice'
+import messagingReducer from './messagingSlice'
+import callReducer from './callSlice'
+
+export const store = configureStore({
+ reducer: {
+ auth: authReducer,
+ messaging: messagingReducer,
+ call: callReducer,
+ },
+})
+
+export type RootState = ReturnType
+export type AppDispatch = typeof store.dispatch
diff --git a/packages/web/src/stores/messagingSlice.ts b/packages/web/src/stores/messagingSlice.ts
new file mode 100644
index 0000000..c3a1d02
--- /dev/null
+++ b/packages/web/src/stores/messagingSlice.ts
@@ -0,0 +1,98 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+
+interface Message {
+ id: string
+ content: string
+ sender_id: string
+ channel_id: string
+ created_at: string
+ updated_at: string
+ reactions?: Record
+}
+
+interface Channel {
+ id: string
+ name: string
+ server_id: string
+ description?: string
+ type: 'text' | 'voice'
+}
+
+interface Server {
+ id: string
+ name: string
+ icon_url?: string
+ owner_id: string
+}
+
+interface MessagingState {
+ servers: Server[]
+ channels: Channel[]
+ messages: Message[]
+ activeServerId: string | null
+ activeChannelId: string | null
+ loading: boolean
+ error: string | null
+}
+
+const initialState: MessagingState = {
+ servers: [],
+ channels: [],
+ messages: [],
+ activeServerId: null,
+ activeChannelId: null,
+ loading: false,
+ error: null,
+}
+
+const messagingSlice = createSlice({
+ name: 'messaging',
+ initialState,
+ reducers: {
+ setServers: (state, action: PayloadAction) => {
+ state.servers = action.payload
+ },
+ setChannels: (state, action: PayloadAction) => {
+ state.channels = action.payload
+ },
+ setMessages: (state, action: PayloadAction) => {
+ state.messages = action.payload
+ },
+ messagesLoaded: (state, action: PayloadAction) => {
+ state.messages = action.payload
+ },
+ addMessage: (state, action: PayloadAction) => {
+ state.messages.push(action.payload)
+ },
+ setActiveServer: (state, action: PayloadAction) => {
+ state.activeServerId = action.payload
+ },
+ setActiveChannel: (state, action: PayloadAction) => {
+ state.activeChannelId = action.payload
+ },
+ setCurrentChannel: (state, action: PayloadAction) => {
+ state.activeChannelId = action.payload
+ },
+ setLoading: (state, action: PayloadAction) => {
+ state.loading = action.payload
+ },
+ setError: (state, action: PayloadAction) => {
+ state.error = action.payload
+ },
+ },
+})
+
+export const {
+ setServers,
+ setChannels,
+ setMessages,
+ messagesLoaded,
+ addMessage,
+ setActiveServer,
+ setActiveChannel,
+ setCurrentChannel,
+ setLoading,
+ setError,
+} = messagingSlice.actions
+
+export default messagingSlice.reducer
diff --git a/packages/web/src/styles/globals.css b/packages/web/src/styles/globals.css
new file mode 100644
index 0000000..6ec02c9
--- /dev/null
+++ b/packages/web/src/styles/globals.css
@@ -0,0 +1,65 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+* {
+ @apply box-border;
+}
+
+html {
+ @apply scroll-smooth;
+}
+
+body {
+ @apply bg-gray-950 text-gray-200 font-sans overflow-x-hidden;
+}
+
+/* Scrollbar Styling */
+::-webkit-scrollbar {
+ @apply w-2;
+}
+
+::-webkit-scrollbar-track {
+ @apply bg-gray-900;
+}
+
+::-webkit-scrollbar-thumb {
+ @apply bg-gray-700 rounded hover:bg-gray-600;
+}
+
+/* Focus Styles */
+:focus-visible {
+ @apply outline-offset-2 outline-purple-500;
+}
+
+/* Animations */
+@keyframes slideIn {
+ from {
+ @apply opacity-0 -translate-x-2;
+ }
+ to {
+ @apply opacity-100 translate-x-0;
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ @apply opacity-0;
+ }
+ to {
+ @apply opacity-100;
+ }
+}
+
+.animate-slide-in {
+ animation: slideIn 0.2s ease-out;
+}
+
+.animate-fade-in {
+ animation: fadeIn 0.2s ease-out;
+}
+
+/* Loading States */
+.skeleton {
+ @apply bg-gray-800 animate-pulse rounded;
+}
diff --git a/packages/web/src/types/index.ts b/packages/web/src/types/index.ts
new file mode 100644
index 0000000..69cd507
--- /dev/null
+++ b/packages/web/src/types/index.ts
@@ -0,0 +1,74 @@
+export interface User {
+ id: string
+ email: string
+ username: string
+ display_name?: string
+ avatar_url?: string
+ verified_domain?: string
+ is_premium: boolean
+ premium_tier: 'free' | 'premium' | 'enterprise'
+ status: 'online' | 'away' | 'dnd' | 'offline'
+ custom_status?: string
+ role?: 'user' | 'admin' | 'moderator'
+ banned?: boolean
+ created_at: string
+ updated_at: string
+}
+
+export interface Server {
+ id: string
+ name: string
+ description?: string
+ icon_url?: string
+ banner_url?: string
+ owner_id: string
+ invite_code: string
+ is_public: boolean
+ member_count: number
+ created_at: string
+ updated_at: string
+}
+
+export interface Channel {
+ id: string
+ server_id: string
+ name: string
+ description?: string
+ channel_type: 'text' | 'voice' | 'announcement' | 'stage'
+ position: number
+ created_at: string
+ updated_at: string
+}
+
+export interface Message {
+ id: string
+ content: string
+ channel_id: string
+ sender_id: string
+ user?: User
+ attachments?: string[]
+ created_at: string
+ updated_at: string
+}
+
+export interface AuthState {
+ user: User | null
+ isAuthenticated: boolean
+ isLoading: boolean
+ error: string | null
+ token: string | null
+}
+
+export interface MessagingState {
+ messages: Message[]
+ currentChannelId: string | null
+ isLoading: boolean
+ error: string | null
+}
+
+export interface CallState {
+ activeCall: any | null
+ incomingCall: any | null
+ isRinging: boolean
+ error: string | null
+}
diff --git a/packages/web/src/utils/helpers.ts b/packages/web/src/utils/helpers.ts
new file mode 100644
index 0000000..d25a08b
--- /dev/null
+++ b/packages/web/src/utils/helpers.ts
@@ -0,0 +1,23 @@
+import { clsx, type ClassValue } from 'clsx'
+
+export function cn(...inputs: ClassValue[]) {
+ return clsx(inputs)
+}
+
+export function formatDate(date: Date | string): string {
+ const d = typeof date === 'string' ? new Date(date) : date
+ return d.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: d.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined,
+ })
+}
+
+export function formatTime(date: Date | string): string {
+ const d = typeof date === 'string' ? new Date(date) : date
+ return d.toLocaleTimeString('en-US', {
+ hour: 'numeric',
+ minute: '2-digit',
+ hour12: true,
+ })
+}
diff --git a/packages/web/tailwind.config.cjs b/packages/web/tailwind.config.cjs
new file mode 100644
index 0000000..7081977
--- /dev/null
+++ b/packages/web/tailwind.config.cjs
@@ -0,0 +1,37 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ discord: {
+ 50: '#f9fafc',
+ 100: '#f1f5f9',
+ 200: '#e2e8f0',
+ 300: '#cbd5e1',
+ 400: '#94a3b8',
+ 500: '#64748b',
+ 600: '#475569',
+ 700: '#334155',
+ 800: '#1e293b',
+ 900: '#0f172a',
+ 950: '#020617',
+ },
+ gaming: {
+ primary: '#5865F2',
+ hover: '#4752C4',
+ dark: '#36393F',
+ darker: '#2C2F33',
+ light: '#DCDDDE',
+ },
+ },
+ backdropBlur: {
+ xs: '2px',
+ },
+ },
+ },
+ plugins: [],
+}
diff --git a/packages/web/tailwind.config.js b/packages/web/tailwind.config.js
new file mode 100644
index 0000000..89ceff7
--- /dev/null
+++ b/packages/web/tailwind.config.js
@@ -0,0 +1,25 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ gray: {
+ 900: "#0f0f0f",
+ 800: "#1a1a1a",
+ 700: "#2d2d2d",
+ 600: "#404040",
+ 500: "#525252",
+ 400: "#737373",
+ 300: "#a3a3a3",
+ 200: "#d4d4d4",
+ 100: "#e5e5e5",
+ },
+ },
+ },
+ },
+ plugins: [],
+}
diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json
new file mode 100644
index 0000000..4be0076
--- /dev/null
+++ b/packages/web/tsconfig.json
@@ -0,0 +1,39 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Paths */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"],
+ "@components/*": ["src/components/*"],
+ "@pages/*": ["src/pages/*"],
+ "@stores/*": ["src/stores/*"],
+ "@services/*": ["src/services/*"],
+ "@utils/*": ["src/utils/*"],
+ "@hooks/*": ["src/hooks/*"]
+ },
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/packages/web/tsconfig.node.json b/packages/web/tsconfig.node.json
new file mode 100644
index 0000000..42872c5
--- /dev/null
+++ b/packages/web/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts
new file mode 100644
index 0000000..bea674c
--- /dev/null
+++ b/packages/web/vite.config.ts
@@ -0,0 +1,36 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import path from 'path'
+
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ '@components': path.resolve(__dirname, './src/components'),
+ '@pages': path.resolve(__dirname, './src/pages'),
+ '@stores': path.resolve(__dirname, './src/stores'),
+ '@services': path.resolve(__dirname, './src/services'),
+ '@utils': path.resolve(__dirname, './src/utils'),
+ '@hooks': path.resolve(__dirname, './src/hooks'),
+ },
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: process.env.VITE_API_URL || 'http://localhost:3000',
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, '/api'),
+ },
+ '/socket.io': {
+ target: process.env.VITE_API_URL || 'http://localhost:3000',
+ ws: true,
+ },
+ },
+ },
+ build: {
+ target: 'ES2020',
+ sourcemap: true,
+ },
+})
diff --git a/src/backend/database/db.js b/src/backend/database/db.js
index 8e69bda..c1878ac 100644
--- a/src/backend/database/db.js
+++ b/src/backend/database/db.js
@@ -1,21 +1,30 @@
-const { Pool } = require('pg');
require('dotenv').config();
-/**
- * PostgreSQL database connection pool
- */
-const pool = new Pool({
- connectionString: process.env.DATABASE_URL,
- max: 20,
- idleTimeoutMillis: 30000,
- connectionTimeoutMillis: 2000,
-});
+// Use in-memory dev DB if DATABASE_URL is not configured
+let pool = null;
+let useDevDb = false;
-// Handle pool errors
-pool.on('error', (err) => {
- console.error('Unexpected error on idle client', err);
- process.exit(-1);
-});
+if (!process.env.DATABASE_URL || process.env.DATABASE_URL.includes('localhost')) {
+ console.log('⚠️ No DATABASE_URL configured. Using in-memory dev database for testing.');
+ console.log('⚠️ This is NOT suitable for production!');
+ useDevDb = true;
+}
+
+if (!useDevDb) {
+ const { Pool } = require('pg');
+ pool = new Pool({
+ connectionString: process.env.DATABASE_URL,
+ max: 20,
+ idleTimeoutMillis: 30000,
+ connectionTimeoutMillis: 2000,
+ });
+
+ // Handle pool errors
+ pool.on('error', (err) => {
+ console.error('Unexpected error on idle client', err);
+ process.exit(-1);
+ });
+}
/**
* Execute a database query
@@ -24,6 +33,11 @@ pool.on('error', (err) => {
* @returns {Promise