From e061d0715d6dc173df14044ed93430d97847f56a Mon Sep 17 00:00:00 2001 From: Jonas Date: Sun, 20 Jul 2025 10:18:40 +1200 Subject: [PATCH 01/27] Remove includeChangeThings --- src/app/login/page.tsx | 5 - src/components/SessionPage/index.tsx | 10 ++ .../SessionResult/ParticipantSessionRow.tsx | 19 ++- .../SessionResult/ResultTabs/index.tsx | 86 ++------------ .../SessionParticipantsTable.tsx | 5 +- .../SessionResult/SessionResultSummary.tsx | 5 +- src/components/login.tsx | 111 ------------------ src/lib/summaryActions.ts | 2 +- src/summary/SummaryUpdateManager.ts | 36 +----- 9 files changed, 40 insertions(+), 239 deletions(-) delete mode 100644 src/app/login/page.tsx delete mode 100644 src/components/login.tsx diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx deleted file mode 100644 index 900bc222..00000000 --- a/src/app/login/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import Login from '@/components/login'; - -export default function LoginPage() { - return ; -} diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index 0295b129..35d145c6 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -1,3 +1,4 @@ +'use client'; import { SessionData } from '@/lib/hooks/useSessionData'; import SessionResultHeader from '@/components/SessionResult/SessionResultHeader'; import SessionResultsOverview from '@/components/SessionResult/SessionResultsOverview'; @@ -5,6 +6,8 @@ import SessionResultsSection from '@/components/SessionResult/SessionResultsSect import { OpenAIMessage } from '@/lib/types'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { SessionStatus } from '@/lib/clientUtils'; +import { useEffect } from 'react'; +import { useSessionStore } from '@/stores/SessionStore'; interface SessionPageProps { data: SessionData; @@ -20,6 +23,13 @@ export default function SessionPage({ chatEntryMessage, }: SessionPageProps) { const { hostData, usersWithChat, stats } = data; + const { addHostData, addUserData } = useSessionStore() + + useEffect(() => { + console.log("Adding data to the store: ", hostData.id) + addHostData(hostData.id, hostData); + addUserData(hostData.id, data.userData); + }, [addHostData, addUserData, hostData, data]) const status = !hostData.active || hostData.final_report_sent diff --git a/src/components/SessionResult/ParticipantSessionRow.tsx b/src/components/SessionResult/ParticipantSessionRow.tsx index 3114967d..d1b35edf 100644 --- a/src/components/SessionResult/ParticipantSessionRow.tsx +++ b/src/components/SessionResult/ParticipantSessionRow.tsx @@ -9,6 +9,8 @@ import { ParticipantsTableData } from './SessionParticipantsTable'; import { Spinner } from '../icons'; import { Switch } from '../ui/switch'; import { MessageSquare, Star, X } from 'lucide-react'; +import * as db from '@/lib/db'; +import { useSessionStore } from '@/stores/SessionStore'; const EMOJI_RATINGS = [ { @@ -40,19 +42,26 @@ const EMOJI_RATINGS = [ export default function ParicipantSessionRow({ tableData, - onIncludeChange, }: { tableData: ParticipantsTableData; - onIncludeChange: (userId: string, included: boolean) => void; }) { const userData = tableData.userData; const [isPopupVisible, setIsPopupVisible] = useState(false); const [messages, setMessages] = useState([]); const [rating, setRating] = useState(null); const [isLoading, setIsLoading] = useState(false); + const { updateUserData } = useSessionStore(); + - const handleIncludeInSummaryUpdate = (updatedIncluded: boolean) => { - onIncludeChange(userData.id, updatedIncluded); + const handleIncludeInSummaryUpdate = async (included: boolean) => { + // Update the store immediately for optimistic update + updateUserData(userData.session_id, userData.id, { include_in_summary: included }); + + // Update the field in the db + await db.updateUserSession(userData.id, { + include_in_summary: included, + last_edit: new Date(), + }); }; const handleViewClick = async () => { @@ -137,7 +146,7 @@ export default function ParicipantSessionRow({ e.stopPropagation()} + onClick={(e) => e.stopPropagation()} // Stops the 'View' popping up /> diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index b7a02dc2..fd42edc6 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -77,77 +77,17 @@ export default function ResultTabs({ useCustomResponses(resourceId); // Use SessionStore for userData with fallback to prop - const { userData: storeUserData, addUserData, updateUserData } = useSessionStore(); - const currentUserData = storeUserData[resourceId] || userData; - - // Initialize store if not present - useEffect(() => { - if (!storeUserData[resourceId] && userData.length > 0) { - addUserData(resourceId, userData); - } - }, [resourceId, userData, storeUserData, addUserData]); - + const { userData: storeUserData } = useSessionStore(); + const currentUserData = + storeUserData[resourceId] && storeUserData[resourceId].length > 0 + ? storeUserData[resourceId] + : userData; // ONLY ON FIRST RENDER userData might be present while the data isn't available in the store yet. + useEffect(() => { SummaryUpdateManager.startPolling(resourceId, 10000); // Poll every 10 seconds return () => SummaryUpdateManager.stopPolling(resourceId); // Clean up on unmount }, [resourceId]); - // User management state - const initialIncluded = currentUserData - .filter((user) => user.include_in_summary) - .map((user) => user.id); - const [userIdsIncludedInSummary, setUpdatedUserIds] = - useState(initialIncluded); - const [initialUserIds, setInitialUserIds] = - useState(initialIncluded); - - // Participant Ids that should be included in the _summary_ and _simscore_ analysis - const updateIncludedInAnalysisList = useCallback( - async (userSessionId: string, included: boolean) => { - const includedIds = currentUserData - .filter((user) => user.include_in_summary) - .map((user) => user.id); - if (included) { - includedIds.push(userSessionId); - } else { - includedIds.splice(includedIds.indexOf(userSessionId), 1); - } - setUpdatedUserIds(includedIds); - - // Update the store immediately for optimistic update - updateUserData(resourceId, userSessionId, { include_in_summary: included }); - - // Update the field in the db and register the edit - await db.updateUserSession(userSessionId, { - include_in_summary: included, - last_edit: new Date(), - }); - - // Compare arrays ignoring order - const haveIncludedUsersChanged = - includedIds.length !== initialUserIds.length || - !includedIds.every((id) => initialUserIds.includes(id)); - - // Register participant edit if users changed - if (haveIncludedUsersChanged) { - console.log('[ResultTabs] Participant changed, registering edit'); - await SummaryUpdateManager.registerEdit(resourceId, { - source: 'participants', - userSessionId, - isProject, - sessionIds: hostData.map(h => h.id), - projectId: isProject ? resourceId : undefined, - }); - } - }, - [currentUserData, setUpdatedUserIds, initialUserIds, hasNewMessages, updateUserData, resourceId, isProject, hostData], - ); - - const hasAnyIncludedUserMessages = useMemo( - () => userIdsIncludedInSummary.length > 0, - [userIdsIncludedInSummary], - ); - // Define available tabs and their visibility conditions in one place const availableTabs = useMemo(() => { console.log('Computing available tabs!'); @@ -158,17 +98,13 @@ export default function ResultTabs({ isVisible: (visibilityConfig.showSummary || visibilityConfig.showSessionRecap || - hasMinimumRole('editor')) && - hasAnyIncludedUserMessages, + hasMinimumRole('editor')), content: ( { - setInitialUserIds(userIdsIncludedInSummary); - }} showSummary={ (hasMinimumRole('editor') || visibilityConfig.showSummary) ?? true } @@ -199,7 +135,6 @@ export default function ResultTabs({ ), }, @@ -280,9 +215,8 @@ export default function ResultTabs({ ]; }, [ hasMinimumRole, - hasAnyIncludedUserMessages, - userIdsIncludedInSummary, responses, + currentUserData ]); // Get visible tabs @@ -368,10 +302,10 @@ export default function ResultTabs({ }; // If all tabs are disabled, or no replies are available yet, show some empty message. - // (For new workspaces, we'll show a placeholder instead of this) + // (For new projects, we'll show a placeholder instead of this) if ( visibleTabs.length === 0 || - (!hasAnyIncludedUserMessages && !draft && !hasMinimumRole('editor')) + (!draft && !hasMinimumRole('editor')) ) { return ( diff --git a/src/components/SessionResult/SessionParticipantsTable.tsx b/src/components/SessionResult/SessionParticipantsTable.tsx index b51e5bee..f07a6695 100644 --- a/src/components/SessionResult/SessionParticipantsTable.tsx +++ b/src/components/SessionResult/SessionParticipantsTable.tsx @@ -25,11 +25,9 @@ export type ParticipantsTableData = { export default function SessionParticipantsTable({ sessionId, userData, - onIncludeInSummaryChange, }: { sessionId: string; userData: UserSession[]; - onIncludeInSummaryChange: (userId: string, included: boolean) => void; }) { const [isGenerateModalOpen, setIsGenerateModalOpen] = useState(false); const [isImportModalOpen, setIsImportModalOpen] = useState(false); @@ -77,7 +75,6 @@ export default function SessionParticipantsTable({ ); }; @@ -111,7 +108,7 @@ export default function SessionParticipantsTable({ tableHeaders={tableHeaders} getTableRow={getTableRow} data={sortableData} - defaultSort={{ column: 'updatedDate', direction: 'desc' }} + defaultSort={{ column: 'createdDate', direction: 'desc' }} /> )} diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index 815bf7d3..ff230ad8 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -12,7 +12,6 @@ interface SessionResultSummaryProps { isProject: boolean; projectId?: string; draft: boolean; - onUpdateSummary: () => void; showSummary?: boolean; showSessionRecap?: boolean; } @@ -22,7 +21,6 @@ export default function SessionResultSummary({ isProject = false, projectId, draft = false, - onUpdateSummary, showSummary = true, showSessionRecap = true, }: SessionResultSummaryProps) { @@ -39,9 +37,8 @@ export default function SessionResultSummary({ const manuallyTriggerSummaryUpdate = () => { setIsUpdating(true); - SummaryUpdateManager.updateNow(hostData[0].id).then(() => { + SummaryUpdateManager.updateNow(hostData[0].id).then((_summary) => { setIsUpdating(false); - onUpdateSummary(); // Summary will be updated automatically via SWR }); }; diff --git a/src/components/login.tsx b/src/components/login.tsx deleted file mode 100644 index ef64f8e1..00000000 --- a/src/components/login.tsx +++ /dev/null @@ -1,111 +0,0 @@ -'use client'; - -import { signIn } from 'next-auth/react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { useState, useEffect, SetStateAction } from 'react'; -import { useRouter, useSearchParams } from 'next/navigation'; - -export default function Login() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - - const router = useRouter(); - const searchParams = useSearchParams(); - - useEffect(() => { - const errorParam = searchParams.get('error'); - if (errorParam) { - setError('Sign-in failed. Check your credentials.'); - } - // Remove all query parameters from the URL - router.replace(window.location.pathname); - }, [searchParams]); - - const handleTestSession = () => { - router.push('/create'); - }; - - const handleEmailChange = (e: { target: { value: SetStateAction; }; }) => { - if (error.length) setError(''); - setEmail(e.target.value); - }; - - const handlePasswordChange = (e: { target: { value: SetStateAction; }; }) => { - if (error.length) setError(''); - setPassword(e.target.value); - }; - - const handleLoginClick = async () => { - if (error.length) setError(''); - const result = await signIn('email', { - email, - password, - redirect: true, - }); - - if (result?.error) { - setError('Sign-in failed. Check your credentials.'); - } - }; - - return ( -
-
-
-
-

Login

-

- Enter your email below to login to your account -

-
- -
-
- - -
-
-
- -
- { - if (e.key === 'Enter') { - handleLoginClick(); - } - }} - required - /> - {error &&
{error}
} -
- - - -
-
-
-
- ); -} diff --git a/src/lib/summaryActions.ts b/src/lib/summaryActions.ts index aca5741d..dd63a8df 100644 --- a/src/lib/summaryActions.ts +++ b/src/lib/summaryActions.ts @@ -41,7 +41,7 @@ export async function checkSummaryNeedsUpdating(resourceId: string, isProject = export async function updateUserLastEdit(userSessionId: string) { if (!userSessionId) { - throw new Error('userSessionId is required'); + throw new Error('userId is required'); } try { diff --git a/src/summary/SummaryUpdateManager.ts b/src/summary/SummaryUpdateManager.ts index 24613713..e95276e9 100644 --- a/src/summary/SummaryUpdateManager.ts +++ b/src/summary/SummaryUpdateManager.ts @@ -35,6 +35,7 @@ class SummaryUpdateManagerClass { this.stopPolling(resourceId); // Ensure no duplicate intervals console.log("Start polling for", resourceId); const poll = async () => { + console.log(`[SummaryUpdateManager] Polling for ${resourceId}`); this.handleUpdateTimes(resourceId); }; @@ -101,32 +102,10 @@ class SummaryUpdateManagerClass { } } - /** - * Marks a resource as having new edits by updating the appropriate timestamp - */ - async registerEdit(resourceId: string, opts: ManagerOpts = {}): Promise { - try { - const { source = 'ui', userSessionId } = opts; - const state = this.getOrCreateState(resourceId); - state.status = RefreshStatus.Outdated; - - if (source === 'participants' && userSessionId) { - await updateUserLastEdit(userSessionId); - } else { - await updateHostLastEdit(resourceId); - } - - state.lastEditTimestamp = Date.now(); // Do this after we've updated, just in case it fails - console.log(`[SummaryUpdateManager] Registered edit for ${resourceId} (source: ${source})`); - } catch (error) { - console.error('Failed to register edit:', error); - } - } - /** * Execute summary update now */ - async updateNow(resourceId: string, opts: ManagerOpts = {}): Promise { + async updateNow(resourceId: string, opts: ManagerOpts = {}): Promise { const state = this.getOrCreateState(resourceId); if (state.isRunning) { console.log(`[SummaryUpdateManager] Update already running for ${resourceId}`); @@ -156,6 +135,7 @@ class SummaryUpdateManagerClass { ); console.log(`[SummaryUpdateManager] Update completed for ${resourceId}`); + return summary; } catch (error) { console.error(`[SummaryUpdateManager] Update failed for ${resourceId}:`, error); @@ -179,13 +159,3 @@ class SummaryUpdateManagerClass { // Export singleton instance export const SummaryUpdateManager = new SummaryUpdateManagerClass(); - -// React hook for convenient access -export function useSummaryUpdate(resourceId: string, opts: ManagerOpts = {}) { - return { - registerEdit: () => SummaryUpdateManager.registerEdit(resourceId, opts), - updateNow: () => SummaryUpdateManager.updateNow(resourceId, opts), - getState: () => SummaryUpdateManager.getState(resourceId), - clearState: () => SummaryUpdateManager.clearState(resourceId) - }; -} From 97f91f7352786d9eed78bdf08608fb3970c60043 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 25 Jul 2025 10:59:05 +1200 Subject: [PATCH 02/27] More Clean Up --- .github/copilot-instructions.md | 57 +++++++++++++++++++ .../SessionResult/ResultTabs/index.tsx | 18 ++---- .../SessionResult/SessionResultHeader.tsx | 3 +- .../SessionResult/SessionResultsSection.tsx | 12 +--- src/components/ShareSettings.tsx | 13 +---- src/lib/clientUtils.ts | 3 - 6 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..cb0fd557 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,57 @@ +# Harmonica Web App Development Guide + +You are an expert full-stack developer proficient in TypeScript, React, Next.js, and modern UI/UX frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI). Your task is to produce the most optimized and maintainable Next.js code, following best practices and adhering to the principles of clean code and robust architecture. + +## Objective +- Create a Next.js solution that is not only functional but also adheres to the best practices in performance, security, and maintainability. + +## Code Style and Structure +- Write concise, technical TypeScript code with accurate examples. +- Use TypeScript with strict typing and proper interfaces/types +- Use functional and declarative programming patterns; avoid classes. +- Favor iteration and modularization over code duplication. +- Use descriptive variable names with auxiliary verbs (e.g., `isLoading`, `hasError`). +- Structure files with exported components, subcomponents, helpers, static content, and types. +- Use lowercase with dashes for directory names (e.g., `components/auth-wizard`). + +## Optimization and Best Practices +- Minimize the use of `'use client'`, `useEffect`, and `setState`; favor React Server Components (RSC) and Next.js SSR features. +- Prefer server actions over API routes +- Use responsive design with a mobile-first approach. +- Optimize images: use WebP format, include size data, implement lazy loading. +- Implement dynamic imports for code splitting and optimization. + +## Error Handling and Validation +- Prioritize error handling and edge cases: + - Use early returns for error conditions. + - Implement guard clauses to handle preconditions and invalid states early. + - Use custom error types for consistent error handling. + +## UI and Styling +- Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling. +- Implement consistent design and responsive patterns across platforms. + +## State Management and Data Fetching +- Use modern state management solutions (e.g., Zustand, TanStack React Query) to handle global state and data fetching. +- Implement validation using Zod for schema validation. + +## Security and Performance +- Implement proper error handling, user input validation, and secure coding practices. +- Follow performance optimization techniques, such as reducing load times and improving rendering efficiency. + +## Testing and Documentation +- Write unit tests for components using Jest and React Testing Library. +- Provide clear and concise comments for complex logic. +- Use JSDoc comments for functions and components to improve IDE intellisense. + +## Methodology +1. **System 2 Thinking**: Approach the problem with analytical rigor. Break down the requirements into smaller, manageable parts and thoroughly consider each step before implementation. +2. **Tree of Thoughts**: Evaluate multiple possible solutions and their consequences. Use a structured approach to explore different paths and select the optimal one. +3. **Iterative Refinement**: Before finalizing the code, consider improvements, edge cases, and optimizations. Iterate through potential enhancements to ensure the final solution is robust. + +**Process**: +1. **Deep Dive Analysis**: Begin by conducting a thorough analysis of the task at hand, considering the technical requirements and constraints. +2. **Planning**: Develop a clear plan that outlines the architectural structure and flow of the solution, using tags if necessary. +3. **Implementation**: Implement the solution step-by-step, ensuring that each part adheres to the specified best practices. +4. **Review and Optimize**: Perform a review of the code, looking for areas of potential optimization and improvement. +5. **Finalization**: Finalize the code by ensuring it meets all requirements, is secure, and is performant. diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index fd42edc6..6e55b7d8 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -32,11 +32,8 @@ import { useSessionStore } from '@/stores/SessionStore'; import { SummaryUpdateManager } from '../../../summary/SummaryUpdateManager'; export interface ResultTabsProps { - hostData: HostSession[]; - userData: UserSession[]; resourceId: string; isWorkspace?: boolean; - hasNewMessages: boolean; sessionIds?: string[]; chatEntryMessage?: OpenAIMessage; visibilityConfig: ResultTabsVisibilityConfig; @@ -55,11 +52,8 @@ const defaultVisibilityConfig: ResultTabsVisibilityConfig = { }; export default function ResultTabs({ - hostData, // zero or more (e.g. if workspace) - userData, // all user data related to all hostData; might be empty resourceId, isWorkspace: isProject = false, - hasNewMessages, visibilityConfig = defaultVisibilityConfig, chatEntryMessage, sessionIds = [], @@ -76,13 +70,11 @@ export default function ResultTabs({ const { responses, addResponse, removeResponse } = useCustomResponses(resourceId); - // Use SessionStore for userData with fallback to prop - const { userData: storeUserData } = useSessionStore(); - const currentUserData = - storeUserData[resourceId] && storeUserData[resourceId].length > 0 - ? storeUserData[resourceId] - : userData; // ONLY ON FIRST RENDER userData might be present while the data isn't available in the store yet. - + const { hostData, userData: currentUserData } = useSessionStore((state) => ({ + hostData: Object.values(state.hostData), + userData: state.userData, + })); + useEffect(() => { SummaryUpdateManager.startPolling(resourceId, 10000); // Poll every 10 seconds return () => SummaryUpdateManager.stopPolling(resourceId); // Clean up on unmount diff --git a/src/components/SessionResult/SessionResultHeader.tsx b/src/components/SessionResult/SessionResultHeader.tsx index 66e1198f..90c832b1 100644 --- a/src/components/SessionResult/SessionResultHeader.tsx +++ b/src/components/SessionResult/SessionResultHeader.tsx @@ -20,9 +20,8 @@ export default function SessionResultHeader({ status, }: SessionResultHeaderProps) { const [isEditing, setIsEditing] = useState(false); - const [newTopic, setNewTopic] = useState(topic); const editableRef = useRef(null); - const addHostData = useSessionStore(state => state.addHostData); + const { addHostData } = useSessionStore(); const { hasMinimumRole } = usePermissions(sessionId); const isEditable = hasMinimumRole('editor'); diff --git a/src/components/SessionResult/SessionResultsSection.tsx b/src/components/SessionResult/SessionResultsSection.tsx index d17a9e42..fd69f52e 100644 --- a/src/components/SessionResult/SessionResultsSection.tsx +++ b/src/components/SessionResult/SessionResultsSection.tsx @@ -1,21 +1,15 @@ 'use client'; import { HostSession, UserSession } from '@/lib/schema'; -import React, { useEffect } from 'react'; -import { mutate } from 'swr'; - -import { checkSummaryAndMessageTimes } from '@/lib/clientUtils'; -import { SummaryUpdateManager } from '../../summary/SummaryUpdateManager'; +import React from 'react'; import ResultTabs from './ResultTabs'; import ExportSection from '../Export/ExportSection'; import { OpenAIMessage } from '@/lib/types'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { usePermissions } from '@/lib/permissions'; -import { usePathname } from 'next/navigation'; import ShareSettings from '../ShareSettings'; - export default function SessionResultsSection({ hostData, userData, @@ -31,10 +25,7 @@ export default function SessionResultsSection({ chatEntryMessage?: OpenAIMessage; }) { const { loading, hasMinimumRole } = usePermissions(resourceId); - const path = usePathname(); const hasMessages = userData.length > 0; - const { hasNewMessages, lastMessage, lastSummaryUpdate } = - checkSummaryAndMessageTimes(hostData, userData); visibilityConfig.showChat = visibilityConfig.showChat && hasMessages; @@ -53,7 +44,6 @@ export default function SessionResultsSection({ hostData={[hostData]} userData={userData} resourceId={resourceId} - hasNewMessages={hasNewMessages} visibilityConfig={visibilityConfig} chatEntryMessage={chatEntryMessage} /> diff --git a/src/components/ShareSettings.tsx b/src/components/ShareSettings.tsx index 9c27d428..8e9d2536 100644 --- a/src/components/ShareSettings.tsx +++ b/src/components/ShareSettings.tsx @@ -20,14 +20,7 @@ import { } from '@/components/ui/select'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Switch } from '@/components/ui/switch'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { Globe, Settings } from 'lucide-react'; +import { Globe } from 'lucide-react'; import { Share2, Loader2, X, UserCog, Copy, Check } from 'lucide-react'; import { useEffect, useState } from 'react'; import { useToast } from 'hooks/use-toast'; @@ -46,8 +39,6 @@ import { import { Role, usePermissions, ROLE_HIERARCHY } from '@/lib/permissions'; import { Invitation, PermissionsTable, User, ResultTabsVisibilityConfig } from '@/lib/schema'; import { encryptId } from '@/lib/encryptionUtils'; -import { Spinner } from './icons'; -import { Description } from '@radix-ui/react-alert-dialog'; interface ShareSettingProps { resourceId: string; @@ -119,8 +110,6 @@ export default function ShareSettings({ viewer: { label: "Viewer", description: "can view and participate" }, none: { label: "None", description: "Should not be able to access this resource" }, }; - - console.log("Assignable roles: ", assignableRoles) const handleOpenChange = (open: boolean) => { if (!open && onClose) { diff --git a/src/lib/clientUtils.ts b/src/lib/clientUtils.ts index bb56997e..e14fd9e7 100644 --- a/src/lib/clientUtils.ts +++ b/src/lib/clientUtils.ts @@ -42,15 +42,12 @@ export function checkSummaryAndMessageTimes( hostData: HostSession, userData: UserSession[], ) { - console.log('Checking for new messages...'); const lastMessage = userData.reduce((latest, user) => { const messageTime = new Date(user.last_edit).getTime(); return messageTime > latest ? messageTime : latest; }, 0); const lastSummaryUpdate = hostData.last_edit.getTime(); const hasNewMessages = lastMessage > lastSummaryUpdate; - console.log('Last message:', lastMessage); - console.log('Last summary update:', lastSummaryUpdate); return { hasNewMessages, lastMessage, lastSummaryUpdate }; } From 6385578e59dbbb68ab6696c5fc176ce937df4332 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 25 Jul 2025 11:43:20 +1200 Subject: [PATCH 03/27] Add workspace to store part 1 --- src/app/workspace/[w_id]/WorkspaceContent.tsx | 17 ++++++++++++++--- src/lib/workspaceData.ts | 3 +-- src/stores/SessionStore.ts | 6 ++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/app/workspace/[w_id]/WorkspaceContent.tsx b/src/app/workspace/[w_id]/WorkspaceContent.tsx index fd440e26..cc971dd2 100644 --- a/src/app/workspace/[w_id]/WorkspaceContent.tsx +++ b/src/app/workspace/[w_id]/WorkspaceContent.tsx @@ -16,6 +16,7 @@ import { PromptSettings } from '@/components/SessionResult/ResultTabs/components import { toast } from 'hooks/use-toast'; import * as db from '@/lib/db'; import { Loader2 } from 'lucide-react'; +import { useSessionStore } from '@/stores/SessionStore'; // Default visibility configuration for workspaces const defaultWorkspaceVisibilityConfig: ResultTabsVisibilityConfig = { @@ -37,23 +38,33 @@ export default function WorkspaceContent({ extendedWorkspaceData, workspaceId, }: WorkspaceContentProps) { - const initialWorkspaceData = extendedWorkspaceData?.workspace; + const initialWorkspaceData = extendedWorkspaceData.workspace; const [workspaceData, setWorkspaceData] = useState( initialWorkspaceData, ); const [extendedData, setExtendedData] = useState(extendedWorkspaceData); - + // Update state when initialWorkspaceData changes useEffect(() => { if (initialWorkspaceData) { setWorkspaceData(initialWorkspaceData); } }, [initialWorkspaceData]); - + // Update extended data when props change useEffect(() => { setExtendedData(extendedWorkspaceData); }, [extendedWorkspaceData]); + + const { addHostData, addUserData, addWorkspaceData} = useSessionStore() + useEffect(() => { + console.log("Adding workspace data to the store: ", extendedWorkspaceData.workspace.id) + addWorkspaceData(workspaceId, extendedWorkspaceData.hostSessions.map(hs => hs.id)) + extendedWorkspaceData.hostSessions.forEach(hostSession => { + addHostData(hostSession.id, hostSession); + addUserData(hostSession.id, extendedWorkspaceData.userData.filter(user => user.session_id === hostSession.id)) + }) + }, [addHostData, addUserData, extendedWorkspaceData]) const { hasMinimumRole, diff --git a/src/lib/workspaceData.ts b/src/lib/workspaceData.ts index 05c865a4..af55f684 100644 --- a/src/lib/workspaceData.ts +++ b/src/lib/workspaceData.ts @@ -68,8 +68,7 @@ export async function fetchWorkspaceData(workspaceId: string): Promise sessionUsers.filter( (user) => - stats[user.session_id][user.id].num_messages > 2 && - user.include_in_summary, + stats[user.session_id][user.id].num_messages > 2 ), ); diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 9b2604f1..0f487804 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -2,6 +2,8 @@ import { create } from 'zustand' import { HostSession, Message, UserSession } from '@/lib/schema'; interface SessionStore { + workspaceData: Record // workspaceId to session IDs + addWorkspaceData: (workspaceId: string, hostSessionIds: string[]) => void hostData: Record addHostData: (id: string, data: HostSession) => void userData: Record @@ -13,6 +15,10 @@ interface SessionStore { } export const useSessionStore = create((set) => ({ + workspaceData: {}, + addWorkspaceData: (workspaceId, hostSessionIds) => set((state) => ({ + workspaceData: { ...state.workspaceData, [workspaceId]: hostSessionIds } + })), hostData: {}, addHostData: (sessionId, data) => set((state) => ({ hostData: { ...state.hostData, [sessionId]: data } From 722204ea1ff1d7db8234f0fe8b03669ee07cfa39 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 25 Jul 2025 12:20:57 +1200 Subject: [PATCH 04/27] Add workspace to store part 2 --- src/app/workspace/[w_id]/WorkspaceContent.tsx | 100 ++++++++---------- .../SessionResult/SessionResultsSection.tsx | 2 - .../workspace/SessionInsightsGrid.tsx | 70 +++++++----- src/stores/SessionStore.ts | 61 +++++++---- 4 files changed, 128 insertions(+), 105 deletions(-) diff --git a/src/app/workspace/[w_id]/WorkspaceContent.tsx b/src/app/workspace/[w_id]/WorkspaceContent.tsx index cc971dd2..883cedac 100644 --- a/src/app/workspace/[w_id]/WorkspaceContent.tsx +++ b/src/app/workspace/[w_id]/WorkspaceContent.tsx @@ -38,33 +38,32 @@ export default function WorkspaceContent({ extendedWorkspaceData, workspaceId, }: WorkspaceContentProps) { - const initialWorkspaceData = extendedWorkspaceData.workspace; - const [workspaceData, setWorkspaceData] = useState( - initialWorkspaceData, - ); - const [extendedData, setExtendedData] = useState(extendedWorkspaceData); - - // Update state when initialWorkspaceData changes + // Prime the store with initial data if needed (SSR hydration) + const { + workspaceMapping, + workspaceData, + addHostData, + addUserData, + addWorkspaceData, + } = useSessionStore((state) => ({ + ...state, + workspaceMapping: state.workspaceMapping[workspaceId], + workspaceData: state.workspaceData[workspaceId] + })); + + // Hydrate store on mount if not already present useEffect(() => { - if (initialWorkspaceData) { - setWorkspaceData(initialWorkspaceData); + if (!workspaceMapping) { + addWorkspaceData(workspaceId, extendedWorkspaceData.workspace,extendedWorkspaceData.hostSessions.map(hs => hs.id)); + extendedWorkspaceData.hostSessions.forEach(hostSession => { + addHostData(hostSession.id, hostSession); + addUserData(hostSession.id, extendedWorkspaceData.userData.filter(user => user.session_id === hostSession.id)); + }); } - }, [initialWorkspaceData]); - - // Update extended data when props change - useEffect(() => { - setExtendedData(extendedWorkspaceData); - }, [extendedWorkspaceData]); - - const { addHostData, addUserData, addWorkspaceData} = useSessionStore() - useEffect(() => { - console.log("Adding workspace data to the store: ", extendedWorkspaceData.workspace.id) - addWorkspaceData(workspaceId, extendedWorkspaceData.hostSessions.map(hs => hs.id)) - extendedWorkspaceData.hostSessions.forEach(hostSession => { - addHostData(hostSession.id, hostSession); - addUserData(hostSession.id, extendedWorkspaceData.userData.filter(user => user.session_id === hostSession.id)) - }) - }, [addHostData, addUserData, extendedWorkspaceData]) + }, [addHostData, addUserData, addWorkspaceData, extendedWorkspaceData, workspaceId, workspaceMapping]); + + // We still need the 'extendedData' object as separate var to keep track of linked sessions & exist status etc... + const [extendedData, setExtendedData] = useState(extendedWorkspaceData); const { hasMinimumRole, @@ -101,10 +100,8 @@ export default function WorkspaceContent({ // Memoize the handleWorkspaceUpdate callback const handleWorkspaceUpdate = useCallback((updates: Workspace) => { - setWorkspaceData((prev) => ({ - ...prev, - ...updates, - })); + // TODO check where this is used! + throw new ReferenceError("This method is not implemented yet; it should update the store directly where it's called. See second-last element in the stack.") }, []); // Memoize the handlePromptChange callback @@ -115,10 +112,8 @@ export default function WorkspaceContent({ const result = await db.updateWorkspace(workspaceId, updateData); if (result) { - setWorkspaceData((prev) => ({ - ...prev, - summary_prompt: newPrompt, - })); + // TODO check where this is used! + throw new ReferenceError("This method is not implemented yet; it should update the store directly where it's called. See second-last element in the stack.") } else { toast({ title: 'Failed to update prompt', @@ -139,23 +134,26 @@ export default function WorkspaceContent({ [workspaceId], ); - // Handle session updates - const handleSessionsUpdate = useCallback(async () => { - try { - // Fetch updated workspace data - const updatedData = await db.getExtendedWorkspaceData(workspaceId); - if (updatedData) { - setExtendedData(updatedData); + const handlePromptChangeSession = async ( + newPrompt: string, + type: 'facilitation' | 'summary', + ) => { + try { + const updateData = + type === 'facilitation' + ? { prompt: newPrompt } + : { summary_prompt: newPrompt }; + + await db.updateHostSession(id, updateData); + } catch (error) { + console.error('Failed to update prompt:', error); + toast({ + title: 'Failed to update prompt', + description: 'An error occurred while updating the prompt.', + variant: 'destructive', + }); } - } catch (error) { - console.error('Failed to update workspace data:', error); - toast({ - title: 'Error', - description: 'Failed to refresh workspace data', - variant: 'destructive', - }); - } - }, [workspaceId]); + }; // Memoize the chat entry message const chatEntryMessage = useMemo( @@ -213,11 +211,8 @@ Here are some questions you might want to ask:
diff --git a/src/components/SessionResult/SessionResultsSection.tsx b/src/components/SessionResult/SessionResultsSection.tsx index fd69f52e..4be3b71a 100644 --- a/src/components/SessionResult/SessionResultsSection.tsx +++ b/src/components/SessionResult/SessionResultsSection.tsx @@ -41,8 +41,6 @@ export default function SessionResultsSection({ )}

Results

[]; - onSessionsUpdate?: () => void; } export default function SessionInsightsGrid({ @@ -37,7 +37,6 @@ export default function SessionInsightsGrid({ workspaceId, showEdit = false, availableSessions = [], - onSessionsUpdate, }: SessionInsightsGridProps) { const router = useRouter(); const { toast } = useToast(); @@ -47,6 +46,7 @@ export default function SessionInsightsGrid({ useState(hostSessions); const [localUserData, setLocalUserData] = useState(userData); const [dialogOpen, setDialogOpen] = useState(false); + const { upsertWorkspaces, upsertHostData, upsertUserData, hostData, userData: storeUserData } = useSessionStore() // Update local state when props change useEffect(() => { @@ -80,33 +80,47 @@ export default function SessionInsightsGrid({ // First, link the sessions to the workspace await linkSessionsToWorkspace(workspaceId, selectedSessions); - // Fetch complete session data and user data for the newly linked sessions - const [newSessionsData, newUserData] = await Promise.all([ - // Fetch session data - Promise.all( - selectedSessions.map(async (sessionId) => { - const sessionData = await db.getHostSessionById(sessionId); - return sessionData; - }), - ), - // Fetch user data - Promise.all( - selectedSessions.map(async (sessionId) => { - const users = await db.getUsersBySessionId(sessionId); - return users; - }), - ), - ]); + // Determine which sessions are not already in the store + const sessionsToFetch = selectedSessions.filter( + (sessionId) => !hostData[sessionId] + ); + + let newSessionsData: HostSession[] = []; + let newUserData: UserSession[][] = []; + + if (sessionsToFetch.length > 0) { + // Fetch only missing session data and user data + [newSessionsData, newUserData] = await Promise.all([ + Promise.all( + sessionsToFetch.map(async (sessionId) => { + const sessionData = await db.getHostSessionById(sessionId); + return sessionData; + }) + ), + Promise.all( + sessionsToFetch.map(async (sessionId) => { + const users = await db.getUsersBySessionId(sessionId); + return users; + }) + ), + ]); + + // Update local state with the new session data + setLocalHostSessions((prev) => [...prev, ...newSessionsData]); - // Update local state with the complete session data - setLocalHostSessions((prev) => [...prev, ...newSessionsData]); + // Update local user data + const flattenedUserData = newUserData.flat(); + setLocalUserData((prev) => [...prev, ...flattenedUserData]); - // Update local user data - const flattenedUserData = newUserData.flat(); - setLocalUserData((prev) => [...prev, ...flattenedUserData]); + // Update the store with new data + newSessionsData.forEach((host, idx) => { + upsertHostData(host.id, host); + upsertUserData(host.id, newUserData[idx]); + }); + } - // Notify parent component to refresh all data - onSessionsUpdate?.(); + // Always update workspace mapping in the store + upsertWorkspaces(workspaceId, {}, selectedSessions); setDialogOpen(false); toast({ @@ -136,8 +150,8 @@ export default function SessionInsightsGrid({ // Perform the actual unlinking await unlinkSessionFromWorkspace(workspaceId, sessionId); - // Notify parent component - onSessionsUpdate?.(); + // Update links in the store (but don't remove fetched sessions, it won't be visible but available faster if the user visits it again...) + upsertWorkspaces(workspaceId, {}, [...selectedSessions.filter(session => session !== sessionId)]); toast({ title: 'Session removed', diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 0f487804..c4c5ff25 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,42 +1,59 @@ import { create } from 'zustand' -import { HostSession, Message, UserSession } from '@/lib/schema'; +import { HostSession, Message, NewWorkspace, UserSession, Workspace } from '@/lib/schema'; interface SessionStore { - workspaceData: Record // workspaceId to session IDs - addWorkspaceData: (workspaceId: string, hostSessionIds: string[]) => void + workspaceData: Record + workspaceMapping: Record // workspaceId to session IDs + upsertWorkspaceData: (workspaceId: string, workspaceData: Workspace | NewWorkspace, hostSessionIds: string[]) => void + upsertWorkspaces: (workspaceId: string, updates: Partial, hostSessionIds: string[]) => void hostData: Record - addHostData: (id: string, data: HostSession) => void + upsertHostData: (id: string, data: HostSession) => void userData: Record - addUserData: (id: string, data: UserSession[]) => void - updateUserData: (sessionId: string, userId: string, updates: Partial) => void + upsertUserData: (sessionId: string, data: UserSession[]) => void messageData: Record - addMessageData: (id: string, data: Message[]) => void + upsertMessageData: (id: string, data: Message[]) => void removeSession: (id: string) => void } export const useSessionStore = create((set) => ({ workspaceData: {}, - addWorkspaceData: (workspaceId, hostSessionIds) => set((state) => ({ - workspaceData: { ...state.workspaceData, [workspaceId]: hostSessionIds } + workspaceMapping: {}, + upsertWorkspaceData: (workspaceId, workspaceData, hostSessionIds) => set((state) => ({ + workspaceData: { ...state.workspaceData, [workspaceId]: workspaceData }, + workspaceMapping: { ...state.workspaceMapping, [workspaceId]: hostSessionIds } + })), + upsertWorkspaces: (workspaceId, updates, hostSessionIds) => set((state) => ({ + workspaceData: { + ...state.workspaceData, + [workspaceId]: { + ...state.workspaceData[workspaceId], + ...updates + } + }, + workspaceMapping: { ...state.workspaceMapping, [workspaceId]: hostSessionIds } })), hostData: {}, - addHostData: (sessionId, data) => set((state) => ({ + upsertHostData: (sessionId, data) => set((state) => ({ hostData: { ...state.hostData, [sessionId]: data } })), userData: {}, - addUserData: (sessionId, data) => set((state) => ({ - userData: { ...state.userData, [sessionId]: data } - })), - updateUserData: (sessionId, userId, updates) => set((state) => ({ - userData: { - ...state.userData, - [sessionId]: state.userData[sessionId]?.map(user => - user.id === userId ? { ...user, ...updates } : user - ) || [] - } - })), + upsertUserData: (sessionId, data) => set((state) => { + const existingUsers = state.userData[sessionId] || []; + // Create a map for quick lookup + const existingMap = new Map(existingUsers.map(user => [user.id, user])); + // Merge: update existing, add new + data.forEach(user => { + existingMap.set(user.id, { ...existingMap.get(user.id), ...user }); + }); + return { + userData: { + ...state.userData, + [sessionId]: Array.from(existingMap.values()), + } + }; + }), messageData: {}, - addMessageData: (threadId, data) => set((state) => ({ + upsertMessageData: (threadId, data) => set((state) => ({ messageData: { ...state.messageData, [threadId]: data } })), removeSession: (sessionId) => set((state) => { From 69ae4601dd33822376c47606fea5a7fafa48733e Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 25 Jul 2025 16:21:05 +1200 Subject: [PATCH 05/27] Tanstack --- package-lock.json | 10484 ++++++++++------ package.json | 2 + src/app/chat/StandaloneChat.tsx | 22 +- src/app/sessions/[id]/layout.tsx | 2 - src/app/test-chat/page.tsx | 15 +- src/app/workspace/[w_id]/WorkspaceContent.tsx | 81 +- src/components/SessionPage/index.tsx | 8 - .../SessionResult/ParticipantSessionRow.tsx | 17 +- .../SessionResult/ResultTabs/index.tsx | 19 +- .../SessionResult/SessionResultHeader.tsx | 8 +- .../workspace/SessionInsightsGrid.tsx | 138 +- src/lib/db.ts | 32 +- src/lib/workspaceActions.ts | 2 + src/stores/SessionStore.ts | 188 +- 14 files changed, 7150 insertions(+), 3868 deletions(-) diff --git a/package-lock.json b/package-lock.json index 942b67f9..63499538 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@radix-ui/react-toast": "^1.2.5", "@radix-ui/react-tooltip": "^1.1.2", "@stripe/stripe-js": "^7.0.0", + "@tanstack/react-query": "^5.83.0", "@types/marked": "^5.0.2", "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.27.3", @@ -65,6 +66,7 @@ "swr": "^2.2.5", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "tanstack": "^1.0.3", "uuid": "^11.1.0", "ws": "^8.18.0", "zod": "^3.23.8", @@ -88,11 +90,80 @@ "node": ">=18" } }, + "node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", + "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/react": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", + "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/ui-utils": "1.2.11", + "swr": "^2.2.5", + "throttleit": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", + "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "engines": { "node": ">=10" }, @@ -245,7 +316,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -257,12 +328,21 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@dnd-kit/accessibility": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", @@ -312,6 +392,16 @@ "react": ">=16.8.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", @@ -798,1051 +888,995 @@ "@hapi/hoek": "^9.0.0" } }, - "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, - "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.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=12" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=12" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, - "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 - }, - "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, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://opencollective.com/libvips" } }, - "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, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@llama-flow/core": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@llama-flow/core/-/core-0.3.9.tgz", - "integrity": "sha512-/s0L16qo/hbF8Ahe5KEjJp6YfomIizp5lyhwgwQ0bomOijP3wX6L2xHnXucPRg5RGxgzpIIlDFW1sTQkjIamqg==", - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.7.0", - "hono": "^4.7.4", - "next": "^15.2.2", - "p-retry": "^6.2.1", - "rxjs": "^7.8.2", - "zod": "^3.24.2" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - }, - "hono": { - "optional": true - }, - "next": { - "optional": true - }, - "p-retry": { - "optional": true - }, - "rxjs": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@llama-flow/llamaindex": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@llama-flow/llamaindex/-/llamaindex-0.0.12.tgz", - "integrity": "sha512-NoUCyVaZTHkdwzllcEY0mzqOkGFTemgpOBHg96fQx5ewZczsy+QpW09pJkMNsLaGQ6JJeBZ4mrBjMkgNUI57Mw==", - "dependencies": { - "@llama-flow/core": "0.3.9" - } - }, - "node_modules/@llamaindex/anthropic": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@llamaindex/anthropic/-/anthropic-0.3.4.tgz", - "integrity": "sha512-OGZ7O/Y2oxSCvvLNip5lO3KeO49B+bZGtcnPoInJAQ1M7EWZUMrHbB/nIkWOQGlraEvPSXKRdjM0hVv/ptr2GQ==", - "dependencies": { - "@anthropic-ai/sdk": "0.37.0", - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29", - "remeda": "^2.17.3" - } - }, - "node_modules/@llamaindex/cloud": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@llamaindex/cloud/-/cloud-4.0.4.tgz", - "integrity": "sha512-8yWeXWxlQwWZAeyMY1Hbgw12U7ghHOVeBzzU9SF4nyQZUiYhTUbBhusePaxex5MDbkB1hLn6xva+fXIOBc74SQ==", - "dependencies": { - "p-retry": "^6.2.1" - }, - "peerDependencies": { - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29" - } - }, - "node_modules/@llamaindex/core": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@llamaindex/core/-/core-0.6.3.tgz", - "integrity": "sha512-65v8+UdoCQqXo/3a8Icl0Tr0NSBeUUKGRFjJLKQY7kDnjly2rCUaOxmQE4y/JSo30cDFdlG4wuPtaAqqvbjtPg==", - "dependencies": { - "@llamaindex/env": "0.1.29", - "@types/node": "^22.9.0", - "magic-bytes.js": "^1.10.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.23.3" - } - }, - "node_modules/@llamaindex/core/node_modules/@types/node": { - "version": "22.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", - "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@llamaindex/core/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==" - }, - "node_modules/@llamaindex/env": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/@llamaindex/env/-/env-0.1.29.tgz", - "integrity": "sha512-GoITt+QLDNIhu2i1sGsPH8tHH13Gxp4i4ofMFlefrHagytvF761MG7nvTAQVIqP9kxBYk4dnSQ3lQnVPFAfMZQ==", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "js-tiktoken": "^1.0.12", - "pathe": "^1.1.2" - }, - "peerDependencies": { - "@huggingface/transformers": "^3.0.2", - "gpt-tokenizer": "^2.5.0" - }, - "peerDependenciesMeta": { - "@huggingface/transformers": { - "optional": true - }, - "gpt-tokenizer": { - "optional": true - } - } - }, - "node_modules/@llamaindex/google": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@llamaindex/google/-/google-0.2.5.tgz", - "integrity": "sha512-qBqu5VKiwCLT9JG/8NR8YgiipIJwyHv3z5eFsHnYFY79TAhc+B4Aptf0I3fgbaTY+eUoxw3RhTg/2ggTeBkwJQ==", - "dependencies": { - "@google-cloud/vertexai": "1.9.0", - "@google/genai": "^0.4.0", - "@google/generative-ai": "0.24.0", - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29" - } - }, - "node_modules/@llamaindex/node-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@llamaindex/node-parser/-/node-parser-2.0.3.tgz", - "integrity": "sha512-Zlfp7xAtkKtlC7GgoLZBVHYu+Gk1+MsfCO6rAJE6qRF9pKIu6ed5tRJQmGxcS8xgnmZfV9DbBq/8DA02CkeiEA==", - "dependencies": { - "html-to-text": "^9.0.5" - }, - "peerDependencies": { - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29", - "tree-sitter": "^0.22.0", - "web-tree-sitter": "^0.24.3" - } - }, - "node_modules/@llamaindex/openai": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@llamaindex/openai/-/openai-0.3.5.tgz", - "integrity": "sha512-KgU9mqK4kgCgt2FvRp+1+TjxYzVqxNkxxsc0Bfu2EZlvjCFFhMT/PgutyGRoCduItfPOjAEPv5lqGF1okN4kwA==", - "dependencies": { - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29", - "openai": "^4.90.0" - } - }, - "node_modules/@llamaindex/workflow": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@llamaindex/workflow/-/workflow-1.0.4.tgz", - "integrity": "sha512-7za+3WflmsolbL52phuUegOspsXQje1i3ThvWkU7g73xFYQHL5Pn5iXfzPe83py7uV0n35aUHioQaOtvT//iOg==", - "dependencies": { - "@llama-flow/llamaindex": "^0.0.12" - }, - "peerDependencies": { - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29", - "zod": "^3.23.8" - } - }, - "node_modules/@neondatabase/serverless": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.10.4.tgz", - "integrity": "sha512-2nZuh3VUO9voBauuh+IGYRhGU/MskWHt1IuZvHcJw6GLjDgtqj/KViKo7SIrLdGLdot7vFbiRRw+BgEy3wT9HA==", - "dependencies": { - "@types/pg": "8.11.6" - } - }, - "node_modules/@next/env": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.28.tgz", - "integrity": "sha512-PAmWhJfJQlP+kxZwCjrVd9QnR5x0R3u0mTXTiZDgSd4h5LdXmjxCCWbN9kq6hkZBOax8Rm3xDW5HagWyJuT37g==" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.28.tgz", - "integrity": "sha512-kzGChl9setxYWpk3H6fTZXXPFFjg7urptLq5o5ZgYezCrqlemKttwMT5iFyx/p1e/JeglTwDFRtb923gTJ3R1w==", + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "darwin" + "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.28.tgz", - "integrity": "sha512-z6FXYHDJlFOzVEOiiJ/4NG8aLCeayZdcRSMjPDysW297Up6r22xw6Ea9AOwQqbNsth8JNgIK8EkWz2IDwaLQcw==", + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "darwin" + "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.28.tgz", - "integrity": "sha512-9ARHLEQXhAilNJ7rgQX8xs9aH3yJSj888ssSjJLeldiZKR4D7N08MfMqljk77fAwZsWwsrp8ohHsMvurvv9liQ==", + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ - "arm64" + "arm" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.0" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.28.tgz", - "integrity": "sha512-p6gvatI1nX41KCizEe6JkF0FS/cEEF0u23vKDpl+WhPe/fCTBeGkEBh7iW2cUM0rvquPVwPWdiUR6Ebr/kQWxQ==", + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.0" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.28.tgz", - "integrity": "sha512-nsiSnz2wO6GwMAX2o0iucONlVL7dNgKUqt/mDTATGO2NY59EO/ZKnKEr80BJFhuA5UC1KZOMblJHWZoqIJddpA==", + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", "cpu": [ - "x64" + "ppc64" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.28.tgz", - "integrity": "sha512-+IuGQKoI3abrXFqx7GtlvNOpeExUH1mTIqCrh1LGFf8DnlUcTmOOCApEnPJUSLrSbzOdsF2ho2KhnQoO0I1RDw==", + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ - "x64" + "s390x" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.28.tgz", - "integrity": "sha512-l61WZ3nevt4BAnGksUVFKy2uJP5DPz2E0Ma/Oklvo3sGj9sw3q7vBWONFRgz+ICiHpW5mV+mBrkB3XEubMrKaA==", + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ - "arm64" + "x64" ], + "license": "Apache-2.0", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.0" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.28.tgz", - "integrity": "sha512-+Kcp1T3jHZnJ9v9VTJ/yf1t/xmtFAc/Sge4v7mVc1z+NYfYzisi8kJ9AsY8itbgq+WgEwMtOpiLLJsUy2qnXZw==", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ - "ia32" + "arm64" ], + "license": "Apache-2.0", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.28.tgz", - "integrity": "sha512-1gCmpvyhz7DkB1srRItJTnmR2UwQPAUXXIg9r0/56g3O8etGmwlX68skKXJOp9EejW3hhv7nSQUJ2raFiz4MoA==", + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], + "license": "Apache-2.0", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@emnapi/runtime": "^1.4.4" }, "engines": { - "node": ">= 8" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@panva/hkdf": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", - "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { - "url": "https://github.com/sponsors/panva" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@qdrant/js-client-rest": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@qdrant/js-client-rest/-/js-client-rest-1.7.0.tgz", - "integrity": "sha512-16O0EQfrrybcPVipodxykr6dMUlBzKW7a63cSDUFVgc5a1AWESwERykwjuvW5KqvKdkPcxZ2NssrvgUO1W3MgA==", + "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==", "dependencies": { - "@qdrant/openapi-typescript-fetch": "^1.2.1", - "@sevinf/maybe": "^0.5.0", - "undici": "^5.26.2" + "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": ">=18.0.0", - "pnpm": ">=8" - }, - "peerDependencies": { - "typescript": ">=4.1" + "node": ">=12" } }, - "node_modules/@qdrant/openapi-typescript-fetch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@qdrant/openapi-typescript-fetch/-/openapi-typescript-fetch-1.2.6.tgz", - "integrity": "sha512-oQG/FejNpItrxRHoyctYvT3rwGZOnK4jr3JdppO/c78ktDvkWiPXPHNsrDf33K9sZdRb6PR7gi4noIapu5q4HA==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { - "node": ">=18.0.0", - "pnpm": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@radix-ui/number": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", - "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==" + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + "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==" }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.13.tgz", - "integrity": "sha512-/uPs78OwxGxslYOG5TKeUsv9fZC0vo376cXSADdKirTmsLJU2au6L3n34c3p6W26rFDDDze/hwy4fYeNd0qdGA==", - "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==", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.13", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=12" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=12" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.13.tgz", - "integrity": "sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==", - "license": "MIT", + "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==", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.8", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=12" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", - "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", - "license": "MIT", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", - "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", - "license": "MIT", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@llama-flow/core": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@llama-flow/core/-/core-0.3.9.tgz", + "integrity": "sha512-/s0L16qo/hbF8Ahe5KEjJp6YfomIizp5lyhwgwQ0bomOijP3wX6L2xHnXucPRg5RGxgzpIIlDFW1sTQkjIamqg==", "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@modelcontextprotocol/sdk": "^1.7.0", + "hono": "^4.7.4", + "next": "^15.2.2", + "p-retry": "^6.2.1", + "rxjs": "^7.8.2", + "zod": "^3.24.2" }, "peerDependenciesMeta": { - "@types/react": { + "@modelcontextprotocol/sdk": { "optional": true }, - "@types/react-dom": { + "hono": { "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", - "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { + }, + "next": { "optional": true }, - "@types/react-dom": { + "p-retry": { "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { + }, + "rxjs": { "optional": true }, - "@types/react-dom": { + "zod": { "optional": true } } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node_modules/@llama-flow/llamaindex": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@llama-flow/llamaindex/-/llamaindex-0.0.12.tgz", + "integrity": "sha512-NoUCyVaZTHkdwzllcEY0mzqOkGFTemgpOBHg96fQx5ewZczsy+QpW09pJkMNsLaGQ6JJeBZ4mrBjMkgNUI57Mw==", + "dependencies": { + "@llama-flow/core": "0.3.9" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", + "node_modules/@llamaindex/anthropic": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@llamaindex/anthropic/-/anthropic-0.3.4.tgz", + "integrity": "sha512-OGZ7O/Y2oxSCvvLNip5lO3KeO49B+bZGtcnPoInJAQ1M7EWZUMrHbB/nIkWOQGlraEvPSXKRdjM0hVv/ptr2GQ==", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@anthropic-ai/sdk": "0.37.0", + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29", + "remeda": "^2.17.3" + } + }, + "node_modules/@llamaindex/cloud": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@llamaindex/cloud/-/cloud-4.0.4.tgz", + "integrity": "sha512-8yWeXWxlQwWZAeyMY1Hbgw12U7ghHOVeBzzU9SF4nyQZUiYhTUbBhusePaxex5MDbkB1hLn6xva+fXIOBc74SQ==", + "dependencies": { + "p-retry": "^6.2.1" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", - "license": "MIT", + "node_modules/@llamaindex/core": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@llamaindex/core/-/core-0.6.3.tgz", + "integrity": "sha512-65v8+UdoCQqXo/3a8Icl0Tr0NSBeUUKGRFjJLKQY7kDnjly2rCUaOxmQE4y/JSo30cDFdlG4wuPtaAqqvbjtPg==", "dependencies": { - "@radix-ui/react-slot": "1.2.2" + "@llamaindex/env": "0.1.29", + "@types/node": "^22.9.0", + "magic-bytes.js": "^1.10.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.3" + } + }, + "node_modules/@llamaindex/core/node_modules/@types/node": { + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@llamaindex/core/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==" + }, + "node_modules/@llamaindex/env": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/@llamaindex/env/-/env-0.1.29.tgz", + "integrity": "sha512-GoITt+QLDNIhu2i1sGsPH8tHH13Gxp4i4ofMFlefrHagytvF761MG7nvTAQVIqP9kxBYk4dnSQ3lQnVPFAfMZQ==", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "js-tiktoken": "^1.0.12", + "pathe": "^1.1.2" }, "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@huggingface/transformers": "^3.0.2", + "gpt-tokenizer": "^2.5.0" }, "peerDependenciesMeta": { - "@types/react": { + "@huggingface/transformers": { "optional": true }, - "@types/react-dom": { + "gpt-tokenizer": { "optional": true } } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", - "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", - "license": "MIT", + "node_modules/@llamaindex/google": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@llamaindex/google/-/google-0.2.5.tgz", + "integrity": "sha512-qBqu5VKiwCLT9JG/8NR8YgiipIJwyHv3z5eFsHnYFY79TAhc+B4Aptf0I3fgbaTY+eUoxw3RhTg/2ggTeBkwJQ==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@google-cloud/vertexai": "1.9.0", + "@google/genai": "^0.4.0", + "@google/generative-ai": "0.24.0", + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", + "node_modules/@llamaindex/node-parser": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@llamaindex/node-parser/-/node-parser-2.0.3.tgz", + "integrity": "sha512-Zlfp7xAtkKtlC7GgoLZBVHYu+Gk1+MsfCO6rAJE6qRF9pKIu6ed5tRJQmGxcS8xgnmZfV9DbBq/8DA02CkeiEA==", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "html-to-text": "^9.0.5" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29", + "tree-sitter": "^0.22.0", + "web-tree-sitter": "^0.24.3" } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", + "node_modules/@llamaindex/openai": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@llamaindex/openai/-/openai-0.3.5.tgz", + "integrity": "sha512-KgU9mqK4kgCgt2FvRp+1+TjxYzVqxNkxxsc0Bfu2EZlvjCFFhMT/PgutyGRoCduItfPOjAEPv5lqGF1okN4kwA==", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29", + "openai": "^4.90.0" } }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", - "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "node_modules/@llamaindex/workflow": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@llamaindex/workflow/-/workflow-1.0.4.tgz", + "integrity": "sha512-7za+3WflmsolbL52phuUegOspsXQje1i3ThvWkU7g73xFYQHL5Pn5iXfzPe83py7uV0n35aUHioQaOtvT//iOg==", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@llama-flow/llamaindex": "^0.0.12" }, "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29", + "zod": "^3.23.8" } }, - "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", - "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "node_modules/@neondatabase/serverless": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.10.4.tgz", + "integrity": "sha512-2nZuh3VUO9voBauuh+IGYRhGU/MskWHt1IuZvHcJw6GLjDgtqj/KViKo7SIrLdGLdot7vFbiRRw+BgEy3wT9HA==", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/pg": "8.11.6" } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", - "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "node_modules/@next/bundle-analyzer": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-15.4.4.tgz", + "integrity": "sha512-/jDIIgLhzErHv36+DBymfMSMrmBq8EPfTGwBCjfz9DZFN9sdUC+4dJcu9OuDKpna+EuznAJnVTrMLd9NSBFVwg==", + "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "webpack-bundle-analyzer": "4.10.1" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node_modules/@next/env": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.28.tgz", + "integrity": "sha512-PAmWhJfJQlP+kxZwCjrVd9QnR5x0R3u0mTXTiZDgSd4h5LdXmjxCCWbN9kq6hkZBOax8Rm3xDW5HagWyJuT37g==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.28.tgz", + "integrity": "sha512-kzGChl9setxYWpk3H6fTZXXPFFjg7urptLq5o5ZgYezCrqlemKttwMT5iFyx/p1e/JeglTwDFRtb923gTJ3R1w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.28.tgz", + "integrity": "sha512-z6FXYHDJlFOzVEOiiJ/4NG8aLCeayZdcRSMjPDysW297Up6r22xw6Ea9AOwQqbNsth8JNgIK8EkWz2IDwaLQcw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", - "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.28.tgz", + "integrity": "sha512-9ARHLEQXhAilNJ7rgQX8xs9aH3yJSj888ssSjJLeldiZKR4D7N08MfMqljk77fAwZsWwsrp8ohHsMvurvv9liQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", - "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.28.tgz", + "integrity": "sha512-p6gvatI1nX41KCizEe6JkF0FS/cEEF0u23vKDpl+WhPe/fCTBeGkEBh7iW2cUM0rvquPVwPWdiUR6Ebr/kQWxQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.28.tgz", + "integrity": "sha512-nsiSnz2wO6GwMAX2o0iucONlVL7dNgKUqt/mDTATGO2NY59EO/ZKnKEr80BJFhuA5UC1KZOMblJHWZoqIJddpA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.28.tgz", + "integrity": "sha512-+IuGQKoI3abrXFqx7GtlvNOpeExUH1mTIqCrh1LGFf8DnlUcTmOOCApEnPJUSLrSbzOdsF2ho2KhnQoO0I1RDw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.28.tgz", + "integrity": "sha512-l61WZ3nevt4BAnGksUVFKy2uJP5DPz2E0Ma/Oklvo3sGj9sw3q7vBWONFRgz+ICiHpW5mV+mBrkB3XEubMrKaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.28.tgz", + "integrity": "sha512-+Kcp1T3jHZnJ9v9VTJ/yf1t/xmtFAc/Sge4v7mVc1z+NYfYzisi8kJ9AsY8itbgq+WgEwMtOpiLLJsUy2qnXZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.28.tgz", + "integrity": "sha512-1gCmpvyhz7DkB1srRItJTnmR2UwQPAUXXIg9r0/56g3O8etGmwlX68skKXJOp9EejW3hhv7nSQUJ2raFiz4MoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 8" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", - "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@qdrant/js-client-rest": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@qdrant/js-client-rest/-/js-client-rest-1.7.0.tgz", + "integrity": "sha512-16O0EQfrrybcPVipodxykr6dMUlBzKW7a63cSDUFVgc5a1AWESwERykwjuvW5KqvKdkPcxZ2NssrvgUO1W3MgA==", + "dependencies": { + "@qdrant/openapi-typescript-fetch": "^1.2.1", + "@sevinf/maybe": "^0.5.0", + "undici": "^5.26.2" + }, + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8" + }, + "peerDependencies": { + "typescript": ">=4.1" + } + }, + "node_modules/@qdrant/openapi-typescript-fetch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@qdrant/openapi-typescript-fetch/-/openapi-typescript-fetch-1.2.6.tgz", + "integrity": "sha512-oQG/FejNpItrxRHoyctYvT3rwGZOnK4jr3JdppO/c78ktDvkWiPXPHNsrDf33K9sZdRb6PR7gi4noIapu5q4HA==", + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.13.tgz", + "integrity": "sha512-/uPs78OwxGxslYOG5TKeUsv9fZC0vo376cXSADdKirTmsLJU2au6L3n34c3p6W26rFDDDze/hwy4fYeNd0qdGA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.13", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -1859,38 +1893,32 @@ } } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", - "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.6", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1901,14 +1929,26 @@ } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", - "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0" + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.13.tgz", + "integrity": "sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -1925,29 +1965,17 @@ } } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", - "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", + "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", + "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -1964,29 +1992,15 @@ } } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", - "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", + "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.2", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-roving-focus": "1.1.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2003,21 +2017,14 @@ } } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", - "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", + "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", + "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2034,13 +2041,14 @@ } } }, - "node_modules/@radix-ui/react-portal": { + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", - "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2057,67 +2065,46 @@ } } }, - "node_modules/@radix-ui/react-presence": { + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-focus-guards": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-radio-group": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.6.tgz", - "integrity": "sha512-1tfTAqnYZNVwSpFhCT273nzK8qGBReeYnNTPspCggqk1fvIrfVxJekIuBFidNivzpdiMqDwVGnQvHqXrRPM4Og==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", + "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2134,17 +2121,14 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2155,10 +2139,10 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2170,11 +2154,15 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2185,13 +2173,13 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-id": { + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2203,37 +2191,27 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-presence": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-primitive": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", - "license": "MIT", + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", "dependencies": { - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", @@ -2250,21 +2228,19 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.9.tgz", - "integrity": "sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ==", - "license": "MIT", + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2281,16 +2257,15 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-collection": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.6.tgz", - "integrity": "sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==", - "license": "MIT", + "node_modules/@radix-ui/react-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2307,14 +2282,10 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-slot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", - "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2325,11 +2296,10 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-callback-ref": { + "node_modules/@radix-ui/react-context": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2340,30 +2310,45 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", + "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2374,29 +2359,64 @@ } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", + "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2407,20 +2427,14 @@ } } }, - "node_modules/@radix-ui/react-roving-focus": { + "node_modules/@radix-ui/react-focus-scope": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", - "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2437,21 +2451,30 @@ } } }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz", - "integrity": "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==", + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", @@ -2467,12 +2490,11 @@ } } }, - "node_modules/@radix-ui/react-select": { + "node_modules/@radix-ui/react-menu": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", - "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", + "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", "dependencies": { - "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", @@ -2484,13 +2506,11 @@ "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2509,12 +2529,21 @@ } } }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.4.tgz", - "integrity": "sha512-2fTm6PSiUm8YPq9W0E4reYuv01EE3aFSzt8edBiXqPHshF8N9+Kymt/k0/R+F3dkY5lQyB/zPtrP82phskLi7w==", + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2531,26 +2560,36 @@ } } }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", - "dependencies": { - "@radix-ui/react-slot": "1.2.0" + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2567,13 +2606,71 @@ } } }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.6.tgz", + "integrity": "sha512-1tfTAqnYZNVwSpFhCT273nzK8qGBReeYnNTPspCggqk1fvIrfVxJekIuBFidNivzpdiMqDwVGnQvHqXrRPM4Og==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2584,12 +2681,43 @@ } } }, - "node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-context": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2601,18 +2729,14 @@ } } }, - "node_modules/@radix-ui/react-switch": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.3.tgz", - "integrity": "sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2629,19 +2753,13 @@ } } }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz", - "integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-primitive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", + "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-roving-focus": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2658,23 +2776,21 @@ } } }, - "node_modules/@radix-ui/react-toast": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", - "integrity": "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.9.tgz", + "integrity": "sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.2" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2691,23 +2807,16 @@ } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", - "integrity": "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-collection": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.6.tgz", + "integrity": "sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.2", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2724,10 +2833,14 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-slot": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2738,13 +2851,11 @@ } } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2755,12 +2866,13 @@ } } }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { @@ -2773,7 +2885,7 @@ } } }, - "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", @@ -2788,13 +2900,11 @@ } } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" - }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2805,10 +2915,14 @@ } } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2819,60 +2933,114 @@ } } }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", - "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", + "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz", + "integrity": "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==", "dependencies": { - "@radix-ui/rect": "1.1.0" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "node_modules/@radix-ui/react-select": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", + "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", - "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", + "node_modules/@radix-ui/react-separator": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.4.tgz", + "integrity": "sha512-2fTm6PSiUm8YPq9W0E4reYuv01EE3aFSzt8edBiXqPHshF8N9+Kymt/k0/R+F3dkY5lQyB/zPtrP82phskLi7w==", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@radix-ui/react-primitive": "2.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2889,745 +3057,2835 @@ } } }, - "node_modules/@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" - }, - "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/@sevinf/maybe": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@sevinf/maybe/-/maybe-0.5.0.tgz", - "integrity": "sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg==" - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dependencies": { - "@hapi/hoek": "^9.0.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", "dependencies": { - "tslib": "^2.6.2" + "@radix-ui/react-slot": "1.2.0" }, - "engines": { - "node": ">=14.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@smithy/types": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", - "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", "dependencies": { - "tslib": "^2.6.2" + "@radix-ui/react-compose-refs": "1.1.2" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" + "@radix-ui/react-compose-refs": "1.1.1" }, - "engines": { - "node": ">=14.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "node_modules/@radix-ui/react-switch": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.3.tgz", + "integrity": "sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" }, - "engines": { - "node": ">=14.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@stripe/stripe-js": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.0.0.tgz", + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz", + "integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", + "integrity": "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", + "integrity": "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", + "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" + }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==", + "license": "MIT" + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@sevinf/maybe": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sevinf/maybe/-/maybe-0.5.0.tgz", + "integrity": "sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg==" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.0.0.tgz", "integrity": "sha512-0AWkP+hoIXB5O34FGY7jh687ZPlOqLqMkJDkiSXcp4TaWWidnxjsZSp0xkjyAWbIz4+j1BFXDAK01Rqb7ceBRA==", "engines": { - "node": ">=12.16" + "node": ">=12.16" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.83.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz", + "integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.83.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz", + "integrity": "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.83.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tiptap/core": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.26.1.tgz", + "integrity": "sha512-fymyd/XZvYiHjBoLt1gxs024xP/LY26d43R1vluYq7AHBL/7DE3ywzy+1GEsGyAv5Je2L0KBhNIR/izbq3Kaqg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.26.1.tgz", + "integrity": "sha512-viQ6AHRhjCYYipKK6ZepBzwZpkuMvO9yhRHeUZDvlSOAh8rvsUTSre0y74nu8QRYUt4a44lJJ6BpphJK7bEgYA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.26.1.tgz", + "integrity": "sha512-zCce9PRuTNhadFir71luLo99HERDpGJ0EEflGm7RN8I1SnNi9gD5ooK42BOIQtejGCJqg3hTPZiYDJC2hXvckQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.26.1.tgz", + "integrity": "sha512-oHevUcZbTMFOTpdCEo4YEDe044MB4P1ZrWyML8CGe5tnnKdlI9BN03AXpI1mEEa5CA3H1/eEckXx8EiCgYwQ3Q==", + "license": "MIT", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.26.1.tgz", + "integrity": "sha512-HHakuV4ckYCDOnBbne088FvCEP4YICw+wgPBz/V2dfpiFYQ4WzT0LPK9s7OFMCN+ROraoug+1ryN1Z1KdIgujQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.26.1.tgz", + "integrity": "sha512-GU9deB1A/Tr4FMPu71CvlcjGKwRhGYz60wQ8m4aM+ELZcVIcZRa1ebR8bExRIEWnvRztQuyRiCQzw2N0xQJ1QQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.26.1.tgz", + "integrity": "sha512-/TDDOwONl0qEUc4+B6V9NnWtSjz95eg7/8uCb8Y8iRbGvI9vT4/znRKofFxstvKmW4URu/H74/g0ywV57h0B+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block-lowlight": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.26.1.tgz", + "integrity": "sha512-yptuTPYAzVMKHUTwNKYveuu0rYHYyFknPz3O2++PWeeBGxkNB+T6LhwZ/JhXceHcZxzlGyka9r2mXR7pslhugw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-code-block": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "highlight.js": "^11", + "lowlight": "^2 || ^3" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.26.1.tgz", + "integrity": "sha512-2P2IZp1NRAE+21mRuFBiP3X2WKfZ6kUC23NJKpn8bcOamY3obYqCt0ltGPhE4eR8n8QAl2fI/3jIgjR07dC8ow==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.26.1.tgz", + "integrity": "sha512-JkDQU2ZYFOuT5mNYb8OiWGwD1HcjbtmX8tLNugQbToECmz9WvVPqJmn7V/q8VGpP81iEECz/IsyRmuf2kSD4uA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.26.1.tgz", + "integrity": "sha512-OJF+H6qhQogVTMedAGSWuoL1RPe3LZYXONuFCVyzHnvvMpK+BP1vm180E2zDNFnn/DVA+FOrzNGpZW7YjoFH1w==", + "license": "MIT", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.26.1.tgz", + "integrity": "sha512-KOiMZc3PwJS3hR0nSq5d0TJi2jkNZkLZElcT6pCEnhRHzPH6dRMu9GM5Jj798ZRUy0T9UFcKJalFZaDxnmRnpg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.26.1.tgz", + "integrity": "sha512-d6uStdNKi8kjPlHAyO59M6KGWATNwhLCD7dng0NXfwGndc22fthzIk/6j9F6ltQx30huy5qQram6j3JXwNACoA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.26.1.tgz", + "integrity": "sha512-KSzL8WZV3pjJG9ke4RaU70+B5UlYR2S6olNt5UCAawM+fi11mobVztiBoC19xtpSVqIXC1AmXOqUgnuSvmE4ZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.26.1.tgz", + "integrity": "sha512-m6YR1gkkauIDo3PRl0gP+7Oc4n5OqDzcjVh6LvWREmZP8nmi94hfseYbqOXUb6RPHIc0JKF02eiRifT4MSd2nw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.26.1.tgz", + "integrity": "sha512-mT6baqOhs/NakgrAeDeed194E/ZJFGL692H0C7f1N7WDRaWxUu2oR0LrnRqSH5OyPjELkzu6nQnNy0+0tFGHHg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.26.1.tgz", + "integrity": "sha512-96+MaYBJebQlR/ik5W72GLUfXdEoxFs+6jsoERxbM5qEdhb7TEnodBFtWZOwgDO27kFd6rSNZuW9r5KJNtljEg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.26.1.tgz", + "integrity": "sha512-pOs6oU4LyGO89IrYE4jbE8ZYsPwMMIiKkYfXcfeD9NtpGNBnjeVXXF5I9ndY2ANrCAgC8k58C3/powDRf0T2yA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.26.1.tgz", + "integrity": "sha512-7yfum5Jymkue/uOSTQPt2SmkZIdZx7t3QhZLqBU7R9ettkdSCBgEGok6N+scJM1R1Zes+maSckLm0JZw5BKYNA==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.26.1.tgz", + "integrity": "sha512-quOXckC73Luc3x+Dcm88YAEBW+Crh3x5uvtQOQtn2GEG91AshrvbnhGRiYnfvEN7UhWIS+FYI5liHFcRKSUKrQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.26.1.tgz", + "integrity": "sha512-UHKNRxq6TBnXMGFSq91knD6QaHsyyOwLOsXMzupmKM5Su0s+CRXEjfav3qKlbb9e4m7D7S/a0aPm8nC9KIXNhQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.26.1.tgz", + "integrity": "sha512-UezvM9VDRAVJlX1tykgHWSD1g3MKfVMWWZ+Tg+PE4+kizOwoYkRWznVPgCAxjmyHajxpCKRXgqTZkOxjJ9Kjzg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.26.1.tgz", + "integrity": "sha512-MBlqbkd+63btY7Qu+SqrXvWjPwooGZDsLTtl7jp52BczBl61cq9yygglt9XpM11TFMBdySgdLHBrLtQ0B7fBlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.26.1.tgz", + "integrity": "sha512-CkoRH+pAi6MgdCh7K0cVZl4N2uR4pZdabXAnFSoLZRSg6imLvEUmWHfSi1dl3Z7JOvd3a4yZ4NxerQn5MWbJ7g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.26.1.tgz", + "integrity": "sha512-LQ63CK53qx2ZsbLTB4mUX0YCoGC0GbYQ82jS3kD+K7M/mb9MCkefvDk6rA8rXF8TjfGnv6o/Fseoot8uhH3qfg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-cell": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.26.1.tgz", + "integrity": "sha512-0P5zY+WGFnULggJkX6+CevmFoBmVv1aUiBBXfcFuLG2mnUsS3QALQTowFtz/0/VbtbjzcOSStaGDHRJxPbk9XQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-header": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.26.1.tgz", + "integrity": "sha512-SAwTW9H+sjVYjoeU5z8pVDMHn3r3FCi+zp2KAxsEsmujcd7qrQdY0cAjQtWjckCq6H3sQkbICa+xlCCd7C8ZAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-row": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.26.1.tgz", + "integrity": "sha512-c4oLrUfj1EVVDpbfKX36v7nnaeI4NxML2KRTQXocvcY65VCe0bPQh8ujpPgPcnKEzdWYdIuAX9RbEAkiYWe8Ww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.26.1.tgz", + "integrity": "sha512-p2n8WVMd/2vckdJlol24acaTDIZAhI7qle5cM75bn01sOEZoFlSw6SwINOULrUCzNJsYb43qrLEibZb4j2LeQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.26.1.tgz", + "integrity": "sha512-t9Nc/UkrbCfnSHEUi1gvUQ2ZPzvfdYFT5TExoV2DTiUCkhG6+mecT5bTVFGW3QkPmbToL+nFhGn4ZRMDD0SP3Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.26.1.tgz", + "integrity": "sha512-8aF+mY/vSHbGFqyG663ds84b+vca5Lge3tHdTMTKazxCnhXR9dn2oQJMnZ78YZvdRbkPkMJJHti9h3K7u2UQvw==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.1", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.23.0", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.4.1", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.37.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.26.1.tgz", + "integrity": "sha512-Zxlwzi1iML7aELa+PyysFD2ncVo2mEcjTkhoDok9iTbMGpm1oU8hgR1i6iHrcSNQLfaRiW6M7HNhZZQPKIC9yw==", + "license": "MIT", + "dependencies": { + "@tiptap/extension-bubble-menu": "^2.26.1", + "@tiptap/extension-floating-menu": "^2.26.1", + "@types/use-sync-external-store": "^0.0.6", + "fast-deep-equal": "^3", + "use-sync-external-store": "^1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.26.1.tgz", + "integrity": "sha512-oziMGCds8SVQ3s5dRpBxVdEKZAmO/O//BjZ69mhA3q4vJdR0rnfLb5fTxSeQvHiqB878HBNn76kNaJrHrV35GA==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^2.26.1", + "@tiptap/extension-blockquote": "^2.26.1", + "@tiptap/extension-bold": "^2.26.1", + "@tiptap/extension-bullet-list": "^2.26.1", + "@tiptap/extension-code": "^2.26.1", + "@tiptap/extension-code-block": "^2.26.1", + "@tiptap/extension-document": "^2.26.1", + "@tiptap/extension-dropcursor": "^2.26.1", + "@tiptap/extension-gapcursor": "^2.26.1", + "@tiptap/extension-hard-break": "^2.26.1", + "@tiptap/extension-heading": "^2.26.1", + "@tiptap/extension-history": "^2.26.1", + "@tiptap/extension-horizontal-rule": "^2.26.1", + "@tiptap/extension-italic": "^2.26.1", + "@tiptap/extension-list-item": "^2.26.1", + "@tiptap/extension-ordered-list": "^2.26.1", + "@tiptap/extension-paragraph": "^2.26.1", + "@tiptap/extension-strike": "^2.26.1", + "@tiptap/extension-text": "^2.26.1", + "@tiptap/extension-text-style": "^2.26.1", + "@tiptap/pm": "^2.26.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "devOptional": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/node": { + "version": "20.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz", + "integrity": "sha512-FM6AOb3khNkNIXPnHFDYaHerSv8uN22C91z098AnGccVu+Pcdhi+pNUFDi0iLmPIsVE0JBD0KVS7mzUYt4nRzQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pg": { + "version": "8.11.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", + "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + }, + "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==", + "optional": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "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", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" + }, + "node_modules/@vercel/analytics": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.4.1.tgz", + "integrity": "sha512-ekpL4ReX2TH3LnrRZTUKjHHNpNy9S1I7QmS+g/RQXoSUQ8ienzosuX7T9djZ/s8zPhBx1mpHP/Rw5875N+zQIQ==", + "peerDependencies": { + "@remix-run/react": "^2", + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, + "node_modules/@vercel/blob": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@vercel/blob/-/blob-0.27.3.tgz", + "integrity": "sha512-WizeAxzOTmv0JL7wOaxvLIU/KdBcrclM1ZUOdSlIZAxsTTTe1jsyBthStLby0Ueh7FnmKYAjLz26qRJTk5SDkQ==", + "dependencies": { + "async-retry": "^1.3.3", + "is-buffer": "^2.0.5", + "is-node-process": "^1.2.0", + "throttleit": "^2.1.0", + "undici": "^5.28.4" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@vercel/postgres": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.10.0.tgz", + "integrity": "sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==", + "dependencies": { + "@neondatabase/serverless": "^0.9.3", + "bufferutil": "^4.0.8", + "ws": "^8.17.1" + }, + "engines": { + "node": ">=18.14" + } + }, + "node_modules/@vercel/postgres-kysely": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@vercel/postgres-kysely/-/postgres-kysely-0.10.0.tgz", + "integrity": "sha512-X1kjLBvjLzE2gdWYSAxFWiRj9SxLYQWZFqIhzNVGG1VqJ8ROxoGiYhPPJrmIC+XACwDuK4SVB0EbFTEcaeO/pQ==", + "dependencies": { + "@vercel/postgres": "0.10.0" + }, + "engines": { + "node": ">=18.14" + }, + "peerDependencies": { + "kysely": "^0.24.2 || ^0.25.0 || ^0.26.0 || ^0.27.0" + } + }, + "node_modules/@vercel/postgres/node_modules/@neondatabase/serverless": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.9.5.tgz", + "integrity": "sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==", + "dependencies": { + "@types/pg": "8.11.6" + } + }, + "node_modules/@videojs/http-streaming": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.17.2.tgz", + "integrity": "sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "aes-decrypter": "^4.0.2", + "global": "^4.4.0", + "m3u8-parser": "^7.2.0", + "mpd-parser": "^1.3.1", + "mux.js": "7.1.0", + "video.js": "^7 || ^8" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "video.js": "^8.19.0" + } + }, + "node_modules/@videojs/vhs-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.1.1.tgz", + "integrity": "sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "global": "^4.4.0" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/@videojs/xhr": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.7.0.tgz", + "integrity": "sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "global": "~4.4.0", + "is-function": "^1.0.1" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.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==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-decrypter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-4.0.2.tgz", + "integrity": "sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "global": "^4.4.0", + "pkcs7": "^1.0.4" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ai": { + "version": "4.3.19", + "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.19.tgz", + "integrity": "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/react": "1.2.12", + "@ai-sdk/ui-utils": "1.2.11", + "@opentelemetry/api": "1.9.0", + "jsondiffpatch": "0.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "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==", + "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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/barbe": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/barbe/-/barbe-2.2.1.tgz", + "integrity": "sha512-yx9qVTYLHqoglkWensOg8lBe/OPiFhcttEsQhs8d4tYvZoQbkGwDQ1R6I2uLijTCCylQXX4nIGVIlcaKf0fO4Q==", + "license": "MIT", + "dependencies": { + "regex-escape": "^3.0.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcp-47": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", + "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz", + "integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-normalize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-1.1.1.tgz", + "integrity": "sha512-jWZ1Jdu3cs0EZdfCkS0UE9Gg01PtxnChjEBySeB+Zo6nkqtFfnvtoQQgP1qU1Oo4qgJgxhTI6Sf9y/pZIhPs0A==", + "license": "MIT", + "dependencies": { + "bcp-47": "^1.0.0", + "bcp-47-match": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47/node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47/node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47/node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bufferutil": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", + "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "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", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001698", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001698.tgz", + "integrity": "sha512-xJ3km2oiG/MbNU8G6zIq6XRZ6HtAOVXsbOrP/blGazi52kc5Yy7b6sDA5O+FbROzRrV7BSTllLHuNvmawYUJjw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/canvg/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } }, - "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "node_modules/codem-isoboxer": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/codem-isoboxer/-/codem-isoboxer-0.3.9.tgz", + "integrity": "sha512-4XOTqEzBWrGOZaMd+sTED2hLpzfBbiQCf1W6OBGkIHqk1D8uwy8WFLazVbdQwfDpQ+vf39lqTGPa9IhWW0roTA==", + "license": "MIT" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constructs": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", + "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", "dev": true }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" + "node_modules/core-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/@types/estree": { + "node_modules/create-gi": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/create-gi/-/create-gi-1.0.4.tgz", + "integrity": "sha512-mjVUIrg+sNv9lrVr+zirmlPX7dr4ExChW5RbKgH1GsSUFDfMJ2NFPTtwdVDGm1YygTfNkbSwrvF8e7XmN09D2w==", + "license": "MIT", + "bin": { + "create-gi": "index.js" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, + "node_modules/crelt": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { - "@types/estree": "*" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "optional": true, "dependencies": { - "@types/unist": "*" + "utrie": "^1.0.2" } }, - "node_modules/@types/lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==" + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/@types/marked": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", - "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==" + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/dashjs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/dashjs/-/dashjs-4.7.4.tgz", + "integrity": "sha512-+hldo25QPP3H/NOwqUrvt4uKdMse60/Gsz9AUAnoYfhga8qHWq4nWiojUosOiigbigkDTCAn9ORcvUaKCvmfCA==", + "license": "BSD-3-Clause", + "dependencies": { + "bcp-47-match": "^1.0.3", + "bcp-47-normalize": "^1.1.1", + "codem-isoboxer": "0.3.9", + "es6-promise": "^4.2.8", + "fast-deep-equal": "2.0.1", + "html-entities": "^1.2.1", + "imsc": "^1.1.5", + "localforage": "^1.7.1", + "path-browserify": "^1.0.1", + "ua-parser-js": "^1.0.37" + } + }, + "node_modules/dashjs/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "license": "MIT" }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dependencies": { - "@types/unist": "*" + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "license": "MIT" }, - "node_modules/@types/node": { - "version": "20.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz", - "integrity": "sha512-FM6AOb3khNkNIXPnHFDYaHerSv8uN22C91z098AnGccVu+Pcdhi+pNUFDi0iLmPIsVE0JBD0KVS7mzUYt4nRzQ==", + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "undici-types": "~5.26.4" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@types/nodemailer": { - "version": "6.4.17", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", - "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" } }, - "node_modules/@types/pg": { - "version": "8.11.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", - "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" } }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/raf": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "optional": true + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, - "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "dev": true, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, - "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==", - "optional": true + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "engines": { + "node": ">=0.3.1" + } }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" }, - "node_modules/@types/ws": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", - "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", - "dev": true, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dependencies": { - "@types/node": "*" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "peer": true }, - "node_modules/@vercel/analytics": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.4.1.tgz", - "integrity": "sha512-ekpL4ReX2TH3LnrRZTUKjHHNpNy9S1I7QmS+g/RQXoSUQ8ienzosuX7T9djZ/s8zPhBx1mpHP/Rw5875N+zQIQ==", - "peerDependencies": { - "@remix-run/react": "^2", - "@sveltejs/kit": "^1 || ^2", - "next": ">= 13", - "react": "^18 || ^19 || ^19.0.0-rc", - "svelte": ">= 4", - "vue": "^3", - "vue-router": "^4" - }, - "peerDependenciesMeta": { - "@remix-run/react": { - "optional": true - }, - "@sveltejs/kit": { - "optional": true - }, - "next": { - "optional": true - }, - "react": { - "optional": true - }, - "svelte": { - "optional": true - }, - "vue": { - "optional": true - }, - "vue-router": { - "optional": true + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" } - } + ] }, - "node_modules/@vercel/blob": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@vercel/blob/-/blob-0.27.3.tgz", - "integrity": "sha512-WizeAxzOTmv0JL7wOaxvLIU/KdBcrclM1ZUOdSlIZAxsTTTe1jsyBthStLby0Ueh7FnmKYAjLz26qRJTk5SDkQ==", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dependencies": { - "async-retry": "^1.3.3", - "is-buffer": "^2.0.5", - "is-node-process": "^1.2.0", - "throttleit": "^2.1.0", - "undici": "^5.28.4" + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=16.14" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/@vercel/postgres": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.10.0.tgz", - "integrity": "sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==", - "dependencies": { - "@neondatabase/serverless": "^0.9.3", - "bufferutil": "^4.0.8", - "ws": "^8.17.1" - }, - "engines": { - "node": ">=18.14" + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" } }, - "node_modules/@vercel/postgres-kysely": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@vercel/postgres-kysely/-/postgres-kysely-0.10.0.tgz", - "integrity": "sha512-X1kjLBvjLzE2gdWYSAxFWiRj9SxLYQWZFqIhzNVGG1VqJ8ROxoGiYhPPJrmIC+XACwDuK4SVB0EbFTEcaeO/pQ==", + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { - "@vercel/postgres": "0.10.0" - }, - "engines": { - "node": ">=18.14" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, - "peerDependencies": { - "kysely": "^0.24.2 || ^0.25.0 || ^0.26.0 || ^0.27.0" + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/@vercel/postgres/node_modules/@neondatabase/serverless": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.9.5.tgz", - "integrity": "sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==", - "dependencies": { - "@types/pg": "8.11.6" + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { - "event-target-shim": "^5.0.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=6.5" + "node": ">= 0.4" } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" } }, - "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, + "node_modules/electron-to-chromium": { + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", + "dev": true + }, + "node_modules/emojer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/emojer/-/emojer-1.1.6.tgz", + "integrity": "sha512-0rNJBx2tBbao/1PIZMWk5Ym2oIlGUUkFOM2ldyWcJkxGr2qGJ+AcY6o+LdAfgEti6jTKTHoq/8Lnx9Ic+dtlNQ==", + "license": "MIT", "dependencies": { - "acorn": "^8.11.0" - }, + "barbe": "^2.2.1", + "match-all": "^1.1.1", + "regex-emoji": "^2.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encriptorjs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encriptorjs/-/encriptorjs-1.0.2.tgz", + "integrity": "sha512-+gnlMVYUAWnmX60CPkyeCoBLiYOLgkbljATUpNFeIUHA6x8O6XdPRprCBc+cnfnqjCtSNA7ozgAHmukbXzaF+Q==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "engines": { - "node": ">=0.4.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { - "node": ">= 14" + "node": ">= 0.4" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { - "node": ">= 8.0.0" + "node": ">= 0.4" } }, - "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==", + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "es-errors": "^1.3.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=8" + "node": ">=18" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true - }, - "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "dependencies": { - "tslib": "^2.0.0" - }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "license": "MIT", - "dependencies": { - "retry": "0.13.1" + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "engines": { - "node": ">= 4.5.0" + "node": ">=6" } }, - "node_modules/attr-accept": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", - "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node": ">=8.6.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "optional": true, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">= 0.6.0" + "node": ">= 6" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "url": "https://github.com/sponsors/fastify" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "opencollective", + "url": "https://opencollective.com/fastify" } ] }, - "node_modules/bignumber.js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", - "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", - "engines": { - "node": "*" + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dependencies": { + "reusify": "^1.0.4" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { - "fill-range": "^7.1.1" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "bin": { - "btoa": "bin/btoa.js" + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 6" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" }, - "node_modules/bufferutil": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", - "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", - "hasInstallScript": true, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "dependencies": { - "node-gyp-build": "^4.3.0" + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" }, "engines": { - "node": ">=6.14.2" + "node": ">= 12.20" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "dependencies": { - "streamsearch": "^1.1.0" + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" }, "engines": { - "node": ">=10.16.0" + "node": ">=14" } }, - "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", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3636,2203 +5894,2528 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001698", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001698.tgz", - "integrity": "sha512-xJ3km2oiG/MbNU8G6zIq6XRZ6HtAOVXsbOrP/blGazi52kc5Yy7b6sDA5O+FbROzRrV7BSTllLHuNvmawYUJjw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/canvg": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", - "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", - "optional": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@types/raf": "^3.4.0", - "core-js": "^3.8.3", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.7", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^6.0.3" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" } }, - "node_modules/canvg/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "optional": true - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "node_modules/github-api-emojis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/github-api-emojis/-/github-api-emojis-1.0.4.tgz", + "integrity": "sha512-KOIHyYLdixrDCGTn2j4P+q0RVxOEhKMGUiqKZmCL+c6/yjIqpCkNitcubxq0H5oOt/I9ZeBrv3zOvjT8st/rKg==", + "license": "MIT" }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "node_modules/glijs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glijs/-/glijs-1.0.1.tgz", + "integrity": "sha512-2D+bFTAm3egMVmaV9ZG/KK8MaWuJq2wVOZu1RY8lZ7l16iB7lihiz8h9TBiHymOm2aatOTVFSOYCmDpp1Kscug==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "license": "MIT", + "peer": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">= 8.10.0" + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "dependencies": { - "is-glob": "^4.0.1" + "gaxios": "^6.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=14.0.0" } }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", "dependencies": { - "clsx": "^2.1.1" + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://polar.sh/cva" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", "dependencies": { - "color-name": "~1.1.4" + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "dependencies": { - "delayed-stream": "~1.0.0" + "@types/hast": "^3.0.0" }, - "engines": { - "node": ">= 0.8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 6" + "node": ">=12.0.0" } }, - "node_modules/constructs": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", - "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", - "dev": true + "node_modules/hls.js": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.7.tgz", + "integrity": "sha512-QW2fnwDGKGc9DwQUGLbmMOz8G48UZK7PVNJPcOUql1b8jubKx4/eMHNP5mGqr6tYlJNDG1g10Lx2U/qPzL6zwQ==", + "license": "Apache-2.0" }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "engines": { - "node": ">= 0.6" + "node_modules/html-encoder-decoder": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/html-encoder-decoder/-/html-encoder-decoder-1.3.11.tgz", + "integrity": "sha512-bDRaOgwtv7trc7YsBoMOPqaFSmI11iPilcPXE1i/blBHe42z8dXQ7yw2Sh9KdmE3CF4fQcIXih69zuFmZi8HoA==", + "license": "MIT", + "dependencies": { + "he": "^1.1.0", + "iterate-object": "^1.3.2", + "regex-escape": "^3.4.2" } }, - "node_modules/core-js": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", - "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "license": "MIT" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "license": "MIT" }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" }, "engines": { - "node": ">= 8" + "node": ">=14" } }, - "node_modules/css-line-break": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "optional": true, - "dependencies": { - "utrie": "^1.0.2" + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" }, "engines": { - "node": ">=4" + "node": ">=8.0.0" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "node_modules/htmled": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/htmled/-/htmled-1.0.1.tgz", + "integrity": "sha512-GfE2GsFY8rpmwdn5PQutIDwel5yeTBdlvKn6mQRSBWzyAgx946UT5K/qD9Fq8Ei5Cw4rqES8TRfSFYbxq+/CDw==", + "license": "MIT" }, - "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" } }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dependencies": { - "ms": "^2.1.3" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 14" } }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" + "ms": "^2.0.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" + "node_modules/imsc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/imsc/-/imsc-1.1.5.tgz", + "integrity": "sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==", + "license": "BSD-2-Clause", + "dependencies": { + "sax": "1.2.1" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" - }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT", + "optional": true }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "binary-extensions": "^2.0.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - ] + ], + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { - "domelementtype": "^2.3.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", - "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" + "node": ">=0.10.0" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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 + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "license": "MIT", + "peer": true }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dependencies": { - "safe-buffer": "^5.0.1" + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", - "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", "engines": { - "node": ">= 0.4" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "license": "MIT" }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": { - "es-errors": "^1.3.0" - }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "engines": { - "node": ">= 0.4" + "node": ">=0.12.0" } }, - "node_modules/esbuild": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", - "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "engines": { - "node": ">=18" + "node": ">=12" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.3", - "@esbuild/android-arm": "0.25.3", - "@esbuild/android-arm64": "0.25.3", - "@esbuild/android-x64": "0.25.3", - "@esbuild/darwin-arm64": "0.25.3", - "@esbuild/darwin-x64": "0.25.3", - "@esbuild/freebsd-arm64": "0.25.3", - "@esbuild/freebsd-x64": "0.25.3", - "@esbuild/linux-arm": "0.25.3", - "@esbuild/linux-arm64": "0.25.3", - "@esbuild/linux-ia32": "0.25.3", - "@esbuild/linux-loong64": "0.25.3", - "@esbuild/linux-mips64el": "0.25.3", - "@esbuild/linux-ppc64": "0.25.3", - "@esbuild/linux-riscv64": "0.25.3", - "@esbuild/linux-s390x": "0.25.3", - "@esbuild/linux-x64": "0.25.3", - "@esbuild/netbsd-arm64": "0.25.3", - "@esbuild/netbsd-x64": "0.25.3", - "@esbuild/openbsd-arm64": "0.25.3", - "@esbuild/openbsd-x64": "0.25.3", - "@esbuild/sunos-x64": "0.25.3", - "@esbuild/win32-arm64": "0.25.3", - "@esbuild/win32-ia32": "0.25.3", - "@esbuild/win32-x64": "0.25.3" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "node_modules/isbot": { + "version": "5.1.22", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.22.tgz", + "integrity": "sha512-RqCFY3cJy3c2y1I+rMn81cfzAR4XJwfPBC+M8kffUjbPzxApzyyv7Tbm1C/gXXq2dSCuD238pKFEWlQMTWsTFw==", "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "node_modules/iterate-object": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.5.tgz", + "integrity": "sha512-eL23u8oFooYTq6TtJKjp2RYjZnCkUYQvC0T/6fJfWykXJ3quvdDdzKZ3CEjy8b3JGOvLTjDYMEMIp5243R906A==", + "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" + "funding": { + "url": "https://github.com/sponsors/isaacs" }, - "engines": { - "node": ">= 6" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "bin": { + "jiti": "bin/jiti.js" + } }, - "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", - "dev": true, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dependencies": { - "reusify": "^1.0.4" + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, - "node_modules/file-selector": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", - "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", - "license": "MIT", + "node_modules/js-tiktoken": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", + "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", "dependencies": { - "tslib": "^2.7.0" - }, - "engines": { - "node": ">= 12" + "base64-js": "^1.5.1" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "bignumber.js": "^9.0.0" } }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, + "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==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "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==" + }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" }, - "engines": { - "node": ">=14" + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/jspdf": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz", + "integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@babel/runtime": "^7.26.7", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.8.1" }, - "engines": { - "node": ">= 6" + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" } }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/kysely": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.5.tgz", + "integrity": "sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=14.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://ko-fi.com/killymxi" } }, - "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "license": "MIT", "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" } }, - "node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "node_modules/linkifyjs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.1.tgz", + "integrity": "sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==", + "license": "MIT" + }, + "node_modules/llamaindex": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/llamaindex/-/llamaindex-0.10.3.tgz", + "integrity": "sha512-vsFfFzKpXOZOTCQ4Dj3iUdUn1lPEDfLthMmj1D7zyC2tEKp7KTCPsdfFxbaYoSlk10VNQu8P+rPAyIaY/e5xsw==", "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" + "@llamaindex/cloud": "4.0.4", + "@llamaindex/core": "0.6.3", + "@llamaindex/env": "0.1.29", + "@llamaindex/node-parser": "2.0.3", + "@llamaindex/openai": "0.3.5", + "@llamaindex/workflow": "1.0.4", + "@types/lodash": "^4.17.7", + "@types/node": "^22.9.0", + "ajv": "^8.17.1", + "lodash": "^4.17.21", + "magic-bytes.js": "^1.10.0" }, "engines": { - "node": ">=14" + "node": ">=18.0.0" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/llamaindex/node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "undici-types": "~6.20.0" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "engines": { - "node": ">=6" + "node_modules/llamaindex/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "license": "Apache-2.0", + "dependencies": { + "lie": "3.1.1" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", - "dev": true, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "peer": true, "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "node_modules/lucide-react": { + "version": "0.381.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.381.0.tgz", + "integrity": "sha512-cft0ywFfHkGprX5pwKyS9jme/ksh9eYAHSZqFRKN0XGp70kia4uqZOTPB+i+O51cqiJlvGLqzMGWnMHaeJTs3g==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/m3u8-parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.2.0.tgz", + "integrity": "sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "global": "^4.4.0" + } + }, + "node_modules/magic-bytes.js": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz", + "integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "glob": "dist/esm/bin.mjs" + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/marked": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.6.tgz", + "integrity": "sha512-Y07CUOE+HQXbVDCGl3LXggqJDbXDP2pArc2C1N1RRMN0ONiShoSsIInMd5Gsxupe7fKLpgimTV+HOJ9r7bA+pg==", + "bin": { + "marked": "bin/marked.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 18" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, + "node_modules/markmind-editor": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/markmind-editor/-/markmind-editor-0.1.0.tgz", + "integrity": "sha512-7XSEuRtr03OVvjdyEWs2eBpfPcC6s3yirogTb8q4Eem4IUf2UWyF8GTiDslo3K0AV3H4QPRCHgIfbJByMbiiyA==", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "@tiptap/extension-bubble-menu": "^2.12.0", + "@tiptap/extension-code-block-lowlight": "^2.12.0", + "@tiptap/extension-floating-menu": "^2.12.0", + "@tiptap/extension-image": "^2.12.0", + "@tiptap/extension-link": "^2.12.0", + "@tiptap/extension-placeholder": "^2.12.0", + "@tiptap/extension-table": "^2.12.0", + "@tiptap/extension-table-cell": "^2.12.0", + "@tiptap/extension-table-header": "^2.12.0", + "@tiptap/extension-table-row": "^2.12.0", + "@tiptap/react": "^2.12.0", + "@tiptap/starter-kit": "^2.12.0", + "ai": "^4.3.15", + "cmdk": "^1.1.1", + "lowlight": "^1.20.0", + "openai": "^4.98.0" }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/markmind-editor/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", "engines": { - "node": ">=10.13.0" + "node": "*" } }, - "node_modules/google-auth-library": { - "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "node_modules/markmind-editor/node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" + "fault": "^1.0.0", + "highlight.js": "~10.7.0" }, - "engines": { - "node": ">=14" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/google-logging-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "node_modules/match-all": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/match-all/-/match-all-1.2.7.tgz", + "integrity": "sha512-qSpsBKarh55r9KyXzFC3xBLRf2GlGasba2em9kbpRsSlGvdTAqjx3QD0r3FKSARiW+OE4iMHYsolM3aX9n5djw==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "engines": { - "node": ">=14" + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "engines": { - "node": ">= 0.4" + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, - "engines": { - "node": ">=14.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "engines": { - "node": ">= 0.4" + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "dependencies": { - "function-bind": "^1.1.2" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", - "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", "dependencies": { - "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", "dependencies": { - "@types/hast": "^3.0.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", - "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" - }, - "engines": { - "node": ">=14" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/html2canvas": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "optional": true, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "css-line-break": "^2.1.0", - "text-segmentation": "^1.0.3" - }, - "engines": { - "node": ">=8.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", { - "type": "github", - "url": "https://github.com/sponsors/fb55" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "ms": "^2.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" - }, - "node_modules/is-alphabetical": { + "node_modules/micromark-util-combine-extensions": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/is-decimal": { + "node_modules/micromark-util-resolve-all": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-network-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", - "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromark-util-types": "^2.0.0" } }, - "node_modules/is-stream": { + "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isbot": { - "version": "5.1.22", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.22.tgz", - "integrity": "sha512-RqCFY3cJy3c2y1I+rMn81cfzAR4XJwfPBC+M8kffUjbPzxApzyyv7Tbm1C/gXXq2dSCuD238pKFEWlQMTWsTFw==", - "engines": { - "node": ">=18" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "bin": { - "jiti": "bin/jiti.js" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "node_modules/micromark-util-subtokenize": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz", + "integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", - "funding": { - "url": "https://github.com/sponsors/panva" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/js-tiktoken": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", - "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", - "dependencies": { - "base64-js": "^1.5.1" - } + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { - "bignumber.js": "^9.0.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "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==" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/jspdf": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz", - "integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "@babel/runtime": "^7.26.7", - "atob": "^2.1.2", - "btoa": "^1.2.1", - "fflate": "^0.8.1" + "mime-db": "1.52.0" }, - "optionalDependencies": { - "canvg": "^3.0.11", - "core-js": "^3.6.0", - "dompurify": "^3.2.4", - "html2canvas": "^1.0.0-rc.5" + "engines": { + "node": ">= 0.6" } }, - "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "peer": true, "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "dom-walk": "^0.1.0" } }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kysely": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.5.tgz", - "integrity": "sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==", + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "node": ">=16 || 14 >=14.17" + }, "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/llamaindex": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/llamaindex/-/llamaindex-0.10.3.tgz", - "integrity": "sha512-vsFfFzKpXOZOTCQ4Dj3iUdUn1lPEDfLthMmj1D7zyC2tEKp7KTCPsdfFxbaYoSlk10VNQu8P+rPAyIaY/e5xsw==", + "node_modules/mpd-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.3.1.tgz", + "integrity": "sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@llamaindex/cloud": "4.0.4", - "@llamaindex/core": "0.6.3", - "@llamaindex/env": "0.1.29", - "@llamaindex/node-parser": "2.0.3", - "@llamaindex/openai": "0.3.5", - "@llamaindex/workflow": "1.0.4", - "@types/lodash": "^4.17.7", - "@types/node": "^22.9.0", - "ajv": "^8.17.1", - "lodash": "^4.17.21", - "magic-bytes.js": "^1.10.0" + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.0.0", + "@xmldom/xmldom": "^0.8.3", + "global": "^4.4.0" }, - "engines": { - "node": ">=18.0.0" + "bin": { + "mpd-to-m3u8-json": "bin/parse.js" } }, - "node_modules/llamaindex/node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", - "dependencies": { - "undici-types": "~6.20.0" + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" } }, - "node_modules/llamaindex/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "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==" }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/mux.js": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-7.1.0.tgz", + "integrity": "sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA==", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "@babel/runtime": "^7.11.2", + "global": "^4.4.0" }, "bin": { - "loose-envify": "cli.js" + "muxjs-transmux": "bin/transmux.js" + }, + "engines": { + "node": ">=8", + "npm": ">=5" } }, - "node_modules/lucide-react": { - "version": "0.381.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.381.0.tgz", - "integrity": "sha512-cft0ywFfHkGprX5pwKyS9jme/ksh9eYAHSZqFRKN0XGp70kia4uqZOTPB+i+O51cqiJlvGLqzMGWnMHaeJTs3g==", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/magic-bytes.js": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz", - "integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==" - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/marked": { - "version": "15.0.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.6.tgz", - "integrity": "sha512-Y07CUOE+HQXbVDCGl3LXggqJDbXDP2pArc2C1N1RRMN0ONiShoSsIInMd5Gsxupe7fKLpgimTV+HOJ9r7bA+pg==", + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { - "marked": "bin/marked.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">= 18" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/next": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.28.tgz", + "integrity": "sha512-QLEIP/kYXynIxtcKB6vNjtWLVs3Y4Sb+EClTC/CSVzdLD1gIuItccpu/n1lhmduffI32iPGEK2cLLxxt28qgYA==", + "dependencies": { + "@next/env": "14.2.28", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, "engines": { - "node": ">= 0.4" + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.28", + "@next/swc-darwin-x64": "14.2.28", + "@next/swc-linux-arm64-gnu": "14.2.28", + "@next/swc-linux-arm64-musl": "14.2.28", + "@next/swc-linux-x64-gnu": "14.2.28", + "@next/swc-linux-x64-musl": "14.2.28", + "@next/swc-win32-arm64-msvc": "14.2.28", + "@next/swc-win32-ia32-msvc": "14.2.28", + "@next/swc-win32-x64-msvc": "14.2.28" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "node_modules/next-auth": { + "version": "5.0.0-beta.25", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.25.tgz", + "integrity": "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==", "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" + "@auth/core": "0.37.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0-0", + "nodemailer": "^6.6.5", + "react": "^18.2.0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } } }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "node_modules/niva-ui": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/niva-ui/-/niva-ui-0.1.0.tgz", + "integrity": "sha512-bE6bYBdY6aUoYaMmuxzCQ0hyAXkkv4OYN1KBaP9mAQwO6oc3AJ5DRh8ABGtziL/1rNAS9JF4l72yvlanMFnBIg==", + "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "@next/bundle-analyzer": "^15.3.3", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-slot": "^1.0.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "commander": "^11.0.0", + "lucide-react": "^0.513.0", + "next": "^15.3.3", + "next-themes": "^0.4.6", + "tailwind-merge": "^2.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "bin": { + "niva": "cli/index.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@next/env": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.4.tgz", + "integrity": "sha512-SJKOOkULKENyHSYXE5+KiFU6itcIb6wSBjgM92meK0HVKpo94dNOLZVdLLuS7/BxImROkGoPsjR4EnuDucqiiA==", + "license": "MIT" + }, + "node_modules/niva-ui/node_modules/@next/swc-darwin-arm64": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.4.tgz", + "integrity": "sha512-eVG55dnGwfUuG+TtnUCt+mEJ+8TGgul6nHEvdb8HEH7dmJIFYOCApAaFrIrxwtEq2Cdf+0m5sG1Np8cNpw9EAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@next/swc-darwin-x64": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.4.tgz", + "integrity": "sha512-zqG+/8apsu49CltEj4NAmCGZvHcZbOOOsNoTVeIXphYWIbE4l6A/vuQHyqll0flU2o3dmYCXsBW5FmbrGDgljQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.4.tgz", + "integrity": "sha512-LRD4l2lq4R+2QCHBQVC0wjxxkLlALGJCwigaJ5FSRSqnje+MRKHljQNZgDCaKUZQzO/TXxlmUdkZP/X3KNGZaw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@next/swc-linux-arm64-musl": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.4.tgz", + "integrity": "sha512-LsGUCTvuZ0690fFWerA4lnQvjkYg9gHo12A3wiPUR4kCxbx/d+SlwmonuTH2SWZI+RVGA9VL3N0S03WTYv6bYg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "node_modules/niva-ui/node_modules/@next/swc-linux-x64-gnu": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.4.tgz", + "integrity": "sha512-aOy5yNRpLL3wNiJVkFYl6w22hdREERNjvegE6vvtix8LHRdsTHhWTpgvcYdCK7AIDCQW5ATmzr9XkPHvSoAnvg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">= 10" } }, - "node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } + "node_modules/niva-ui/node_modules/@next/swc-linux-x64-musl": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.4.tgz", + "integrity": "sha512-FL7OAn4UkR8hKQRGBmlHiHinzOb07tsfARdGh7v0Z0jEJ3sz8/7L5bR23ble9E6DZMabSStqlATHlSxv1fuzAg==", + "cpu": [ + "x64" ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", - "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } + "node_modules/niva-ui/node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.4.tgz", + "integrity": "sha512-eEdNW/TXwjYhOulQh0pffTMMItWVwKCQpbziSBmgBNFZIIRn2GTXrhrewevs8wP8KXWYMx8Z+mNU0X+AfvtrRg==", + "cpu": [ + "arm64" ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } + "node_modules/niva-ui/node_modules/@next/swc-win32-x64-msvc": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.4.tgz", + "integrity": "sha512-SE5pYNbn/xZKMy1RE3pAs+4xD32OI4rY6mzJa4XUkp/ItZY+OMjIgilskmErt8ls/fVJ+Ihopi2QIeW6O3TrMw==", + "cpu": [ + "x64" ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/niva-ui/node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ], - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/niva-ui/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-menu": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ] + } }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-popper": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ] + } }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz", - "integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "node_modules/niva-ui/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "@types/react-dom": { + "optional": true } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ] + } }, - "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "node_modules/niva-ui/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } - ] + } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "node_modules/niva-ui/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">=8.6" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" + "node_modules/niva-ui/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" + "node_modules/niva-ui/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">= 0.6" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "node_modules/niva-ui/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "@radix-ui/rect": "1.1.1" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" + "node_modules/niva-ui/node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "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==" + "node_modules/niva-ui/node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, + "node_modules/niva-ui/node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "tslib": "^2.8.0" } }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, + "node_modules/niva-ui/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=16" } }, - "node_modules/next": { - "version": "14.2.28", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.28.tgz", - "integrity": "sha512-QLEIP/kYXynIxtcKB6vNjtWLVs3Y4Sb+EClTC/CSVzdLD1gIuItccpu/n1lhmduffI32iPGEK2cLLxxt28qgYA==", + "node_modules/niva-ui/node_modules/lucide-react": { + "version": "0.513.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.513.0.tgz", + "integrity": "sha512-CJZKq2g8Y8yN4Aq002GahSXbG2JpFv9kXwyiOAMvUBv7pxeOFHUWKB0mO7MiY4ZVFCV4aNjv2BJFq/z3DgKPQg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/niva-ui/node_modules/next": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.4.4.tgz", + "integrity": "sha512-kNcubvJjOL9yUOfwtZF3HfDhuhp+kVD+FM2A6Tyua1eI/xfmY4r/8ZS913MMz+oWKDlbps/dQOWdDricuIkXLw==", + "license": "MIT", "dependencies": { - "@next/env": "14.2.28", - "@swc/helpers": "0.5.5", - "busboy": "1.6.0", + "@next/env": "15.4.4", + "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.28", - "@next/swc-darwin-x64": "14.2.28", - "@next/swc-linux-arm64-gnu": "14.2.28", - "@next/swc-linux-arm64-musl": "14.2.28", - "@next/swc-linux-x64-gnu": "14.2.28", - "@next/swc-linux-x64-musl": "14.2.28", - "@next/swc-win32-arm64-msvc": "14.2.28", - "@next/swc-win32-ia32-msvc": "14.2.28", - "@next/swc-win32-x64-msvc": "14.2.28" + "@next/swc-darwin-arm64": "15.4.4", + "@next/swc-darwin-x64": "15.4.4", + "@next/swc-linux-arm64-gnu": "15.4.4", + "@next/swc-linux-arm64-musl": "15.4.4", + "@next/swc-linux-x64-gnu": "15.4.4", + "@next/swc-linux-x64-musl": "15.4.4", + "@next/swc-win32-arm64-msvc": "15.4.4", + "@next/swc-win32-x64-msvc": "15.4.4", + "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -5842,47 +8425,15 @@ "@playwright/test": { "optional": true }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-auth": { - "version": "5.0.0-beta.25", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.25.tgz", - "integrity": "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==", - "dependencies": { - "@auth/core": "0.37.2" - }, - "peerDependencies": { - "@simplewebauthn/browser": "^9.0.1", - "@simplewebauthn/server": "^9.0.2", - "next": "^14.0.0-0 || ^15.0.0-0", - "nodemailer": "^6.6.5", - "react": "^18.2.0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@simplewebauthn/browser": { - "optional": true - }, - "@simplewebauthn/server": { + "babel-plugin-react-compiler": { "optional": true }, - "nodemailer": { + "sass": { "optional": true } } }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/next/node_modules/postcss": { + "node_modules/niva-ui/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", @@ -5900,6 +8451,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -5909,6 +8461,39 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/niva-ui/node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -5994,7 +8579,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6008,6 +8592,29 @@ "node": ">=0.10.0" } }, + "node_modules/notion-design-system": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/notion-design-system/-/notion-design-system-1.0.0.tgz", + "integrity": "sha512-nvaCI/tutdotKFH7MAFH7Xf1/pM9D7QYSYihG8o/hbBg8ZGI/lgiswmjjTU4qxjIQ8mbdII9priR9HlTeoovvA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "lucide-react": "^0.294.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/notion-design-system/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/oauth4webapi": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.17.0.tgz", @@ -6057,9 +8664,10 @@ } }, "node_modules/openai": { - "version": "4.96.2", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.96.2.tgz", - "integrity": "sha512-R2XnxvMsizkROr7BV3uNp1q/3skwPZ7fmPjO1bXLnfB4Tu5xKxrT1EVwzjhxn0MZKBKAvOaGWS63jTMN6KrIXA==", + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", @@ -6093,6 +8701,15 @@ "undici-types": "~5.26.4" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/openid-client": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", @@ -6123,6 +8740,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, "node_modules/p-retry": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", @@ -6142,8 +8765,7 @@ "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 + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/parse-entities": { "version": "4.0.2", @@ -6180,11 +8802,22 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/parsemarkjs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parsemarkjs/-/parsemarkjs-1.0.1.tgz", + "integrity": "sha512-7q9JNgiXGlMTr/CmBsNnW4+AmDSC+682izfsrIm4bm56NIv6PnynWy0U/8QIvWgsojqdIMcTbrcHvLUPhhl5FA==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -6192,14 +8825,12 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6214,8 +8845,7 @@ "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/pathe": { "version": "1.1.2", @@ -6412,7 +9042,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -6424,7 +9053,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6433,16 +9061,27 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, "engines": { "node": ">= 6" } }, + "node_modules/pkcs7": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.4.tgz", + "integrity": "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5" + }, + "bin": { + "pkcs7": "bin/cli.js" + } + }, "node_modules/postcss": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6470,7 +9109,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -6487,7 +9125,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -6506,7 +9143,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6541,7 +9177,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6566,7 +9201,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6578,8 +9212,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postgres-array": { "version": "3.0.2", @@ -6671,6 +9304,16 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -6690,6 +9333,210 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/prosemirror-changeset": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", + "integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", + "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.0.tgz", + "integrity": "sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz", + "integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==", + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz", + "integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==", + "license": "MIT", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.2", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.2.tgz", + "integrity": "sha512-BVypCAJ4SL6jOiTsDffP3Wp6wD69lRhI4zg/iT8JXjp3ccZFiq5WyguxvMKmdKFC3prhaig7wSr8dneDToHE1Q==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz", + "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.7.1.tgz", + "integrity": "sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.25.0", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.10.3", + "prosemirror-view": "^1.39.1" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "license": "MIT", + "dependencies": { + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz", + "integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.40.1.tgz", + "integrity": "sha512-pbwUjt3G7TlsQQHDiYSupWBhJswpLVB09xXm1YiJPdkjkh9Pe7Y51XdLh5VWIZmROLY8UpUpG03lkdhm9lzIBA==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qrcode.react": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.2.0.tgz", @@ -6712,11 +9559,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queryxjs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/queryxjs/-/queryxjs-1.0.0.tgz", + "integrity": "sha512-N+I3rq6nRcO5wurtYV7kOnjDqKGM/2Nh6yLufeTuB434Jyo9DZTy/IL4b7+bbAk0bulzXyUVyo0z9zWPV4XdYw==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -6902,7 +9754,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -6911,7 +9762,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -6919,6 +9769,18 @@ "node": ">=8.10.0" } }, + "node_modules/regex-emoji": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/regex-emoji/-/regex-emoji-2.0.9.tgz", + "integrity": "sha512-jtCSFzaIL/MbesSo9BDhYbJGOss/Eqb7U4uLhEDCST7eVIHmON+y6bkEWTCGgmZOERNOyIvrRr/rDMZAAuS9iA==", + "license": "MIT" + }, + "node_modules/regex-escape": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/regex-escape/-/regex-escape-3.4.11.tgz", + "integrity": "sha512-051l4Hl/0HoJwTvNztrWVjoxLiseSfCrDgWqwR1cnGM/nyQSeIjmvti5zZ7HzOmsXDPaJ2k0iFxQ6/WNpJD5wQ==", + "license": "MIT" + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -6970,7 +9832,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -7008,7 +9869,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7023,11 +9883,16 @@ "node": ">= 0.8.15" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -7065,6 +9930,51 @@ } ] }, + "node_modules/safestore": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/safestore/-/safestore-1.0.0-beta.1.tgz", + "integrity": "sha512-5m9MYHW5s/ixVbKxYN86Mor6ysPwAgmysk/IJur+r+UH+sKH5puGBmGRlCY2RBaRUcL4JZhcUudKIblVIfF3yQ==", + "license": "MIT", + "bin": { + "lockbox": "file-encryption-cli.js", + "safe": "file-encryption-cli.js", + "safestore": "file-encryption-cli.js", + "safestore2": "file-encryption-cli.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sanskrit-lang": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sanskrit-lang/-/sanskrit-lang-0.1.1.tgz", + "integrity": "sha512-zen2c1tJ+sW6vmQ2/bBOD5dmoaWbiiUPv6JdPaXOym7e/Vwyhl2ovuI5QiPg/hvwNq+1WF4iqvKT69sRCMLFuw==", + "license": "MIT", + "dependencies": { + "commander": "^8.0.0" + }, + "bin": { + "sanskrit": "src/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sanskrit-lang/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -7073,6 +9983,12 @@ "loose-envify": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/selderee": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", @@ -7084,11 +10000,66 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7100,11 +10071,82 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "license": "MIT", + "dependencies": { + "commander": "^9.0.0" + }, + "bin": { + "showdown": "bin/showdown.js" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" + } + }, + "node_modules/showdown-emoji": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/showdown-emoji/-/showdown-emoji-3.0.0.tgz", + "integrity": "sha512-ZSEmtkMLJmm+2V0kjHwD6lgZM+t3X69+7fwAmKrthxO9CsyMDQ6cFb0wpLQX+LTDNb+HlCszpBLfBo/RG/l5Hw==", + "license": "MIT", + "dependencies": { + "emojer": "^1.1.5", + "github-api-emojis": "^1.0.3" + } + }, + "node_modules/showdown-extensions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/showdown-extensions/-/showdown-extensions-1.0.1.tgz", + "integrity": "sha512-0Phk3NdjCHhG5bh9R8z7yA2UtcI5tD+OzlngXYprp9884QZSdDGwAvFFvsl2j2CpeJI8W218JQ4l6JLbvRLKNA==", + "license": "MIT", + "dependencies": { + "showdown-emoji": "^3.0.0", + "showdown-highlight": "^3.1.0", + "showdown-target-blank": "^1.0.2" + } + }, + "node_modules/showdown-highlight": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/showdown-highlight/-/showdown-highlight-3.1.0.tgz", + "integrity": "sha512-wrTxtE63L/bpW5A2Uy/AO1gblXnNHK/cDL6LszECOoCdMJKWTj0/4n4I/pmqub+3H3KCPVDDvtXpCArnT/heFA==", + "license": "MIT", + "dependencies": { + "highlight.js": "^11.5.0", + "html-encoder-decoder": "^1.3.9", + "showdown": "^2.0.3" + } + }, + "node_modules/showdown-target-blank": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/showdown-target-blank/-/showdown-target-blank-1.0.2.tgz", + "integrity": "sha512-Yxvg/ixZ34Wwk5iaY/ZPzxnc0f1oQxzhg4lLezavHfhPZumIMQilRlfFTaIKW1tv3nIzA+FNBktuAjCgCQ242w==", + "license": "BSD", + "dependencies": { + "showdown": "1.1.0" + } + }, + "node_modules/showdown-target-blank/node_modules/showdown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.1.0.tgz", + "integrity": "sha512-qg3WJ40tVdXoEnccka5R+denlJyb+0iv24z6v99y2G7Ew5mrskYUrR7YYqf3Z3dV/4bZbNWcCJhAas7u1vKMTA==", + "license": "BSD-2-Clause" + }, + "node_modules/showdown/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -7177,7 +10219,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -7185,6 +10226,36 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sopplayer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sopplayer/-/sopplayer-1.0.0.tgz", + "integrity": "sha512-lgbWHqQdbsAy7WEEWyi6ciYaPXCa80pprTZxIRN5pdm3OSZBnC2GacNwZ4allmw7AcwHgcyO+D7uPKhwsLiIQQ==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -7224,6 +10295,12 @@ "node": ">=0.1.14" } }, + "node_modules/steamlit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/steamlit/-/steamlit-1.0.0.tgz", + "integrity": "sha512-HrUNAseHT82W/8OCL1wAAwPlpDglGbdWmKn27Aejc7QFh9JZGn6OTulLSTKw179FOwX5MQTWy+yl8nZUqVjEww==", + "license": "MIT" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -7236,7 +10313,6 @@ "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, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7251,7 +10327,6 @@ "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, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7278,7 +10353,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7291,7 +10365,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7345,7 +10418,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -7367,7 +10439,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -7409,7 +10480,6 @@ "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -7454,11 +10524,49 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "engines": { "node": ">= 6" } }, + "node_modules/tanstack": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tanstack/-/tanstack-1.0.3.tgz", + "integrity": "sha512-5+Iy3/oQd3u81Sg+x2pt3fI+GAt02vqtKzO2hdWQDSFG2rnvY3yNr17xFhDRmg9iYoOgfWHRzhm34qFLpWWR6A==", + "license": "MIT", + "dependencies": { + "create-gi": "^1.0.4", + "encriptorjs": "^1.0.2", + "glijs": "^1.0.1", + "htmled": "^1.0.1", + "markmind-editor": "^0.1.0", + "niva-ui": "^0.1.0", + "notion-design-system": "^1.0.0", + "parsemarkjs": "^1.0.1", + "queryxjs": "^1.0.0", + "safestore": "^1.0.0-beta.1", + "sanskrit-lang": "^0.1.1", + "showdown-extensions": "^1.0.1", + "sopplayer": "^1.0.0", + "steamlit": "^1.0.0", + "tanstack": "^1.0.1", + "terabox": "^1.0.0", + "vibly": "^0.1.2", + "webscrapperjs": "^1.0.0" + }, + "bin": { + "create-tanstack-app": "bin/create-tanstack-app.js", + "tanstack": "bin/create-tanstack-app.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/terabox": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/terabox/-/terabox-1.0.0.tgz", + "integrity": "sha512-SCqf1fVF/cHfW4MkvcUP7vN7i1YikmyaVNWin/fOJdFzhJmQ16tcQjsJaHQ7X5pizGVkX5ZorlDZPriLqxFHmg==", + "license": "ISC" + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -7472,7 +10580,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -7481,7 +10588,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -7501,11 +10607,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -7513,6 +10627,27 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tree-sitter": { + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", + "integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -7534,14 +10669,13 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7584,7 +10718,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/tslib": { "version": "2.8.1", @@ -7625,7 +10759,6 @@ "version": "5.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7634,6 +10767,38 @@ "node": ">=14.17" } }, + "node_modules/ua-parser-js": { + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/undici": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", @@ -7818,8 +10983,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utrie": { "version": "1.0.2", @@ -7846,7 +11010,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/vfile": { "version": "6.0.3", @@ -7874,6 +11038,80 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vibly": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/vibly/-/vibly-0.1.2.tgz", + "integrity": "sha512-hPjBbQOD6NtZ6pI1MbppwKcY4DiJLh4zxu8aEGM9Icgttzzbhc2DesjHKrFPGtThX1FcMRKnrSqnRnFMTJARzw==", + "license": "MIT", + "dependencies": { + "dashjs": "^4.7.1", + "hls.js": "^1.4.12" + }, + "peerDependencies": { + "video.js": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/video.js": { + "version": "8.23.3", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-8.23.3.tgz", + "integrity": "sha512-Toe0VLlDZcUhiaWfcePS1OEdT3ATfktm0hk/PELfD7zUoPDHeT+cJf/wZmCy5M5eGVwtGUg25RWPCj1L/1XufA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/http-streaming": "^3.17.0", + "@videojs/vhs-utils": "^4.1.1", + "@videojs/xhr": "2.7.0", + "aes-decrypter": "^4.0.2", + "global": "4.4.0", + "m3u8-parser": "^7.2.0", + "mpd-parser": "^1.3.1", + "mux.js": "^7.0.1", + "videojs-contrib-quality-levels": "4.1.0", + "videojs-font": "4.2.0", + "videojs-vtt.js": "0.15.5" + } + }, + "node_modules/videojs-contrib-quality-levels": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.1.0.tgz", + "integrity": "sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "global": "^4.4.0" + }, + "engines": { + "node": ">=16", + "npm": ">=8" + }, + "peerDependencies": { + "video.js": "^8" + } + }, + "node_modules/videojs-font": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-4.2.0.tgz", + "integrity": "sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/videojs-vtt.js": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz", + "integrity": "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "global": "^4.3.1" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", @@ -7882,16 +11120,85 @@ "node": ">= 14" } }, + "node_modules/web-tree-sitter": { + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.7.tgz", + "integrity": "sha512-CdC/TqVFbXqR+C51v38hv6wOPatKEUGxa39scAeFSm98wIhZxAYonhRQPSMmfZ2w7JDI0zQDdzdmgtNk06/krQ==", + "license": "MIT", + "peer": true + }, "node_modules/web-vitals": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", + "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "is-plain-object": "^5.0.0", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webscrapperjs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/webscrapperjs/-/webscrapperjs-1.0.0.tgz", + "integrity": "sha512-weNg8xp7bgX2WVh2lc8e2mp1X7NguEI04v+jNbjyMI4TCg+f2qhziqpfVvPi+nGqDrYeIx+5srN+Xz2dB7ALYw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -7907,7 +11214,6 @@ "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, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7952,7 +11258,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, @@ -7964,7 +11269,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -8021,6 +11326,21 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "node_modules/niva-ui/node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.28", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.28.tgz", + "integrity": "sha512-+Kcp1T3jHZnJ9v9VTJ/yf1t/xmtFAc/Sge4v7mVc1z+NYfYzisi8kJ9AsY8itbgq+WgEwMtOpiLLJsUy2qnXZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/package.json b/package.json index 916e9dbb..ada67b52 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@radix-ui/react-toast": "^1.2.5", "@radix-ui/react-tooltip": "^1.1.2", "@stripe/stripe-js": "^7.0.0", + "@tanstack/react-query": "^5.83.0", "@types/marked": "^5.0.2", "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.27.3", @@ -67,6 +68,7 @@ "swr": "^2.2.5", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "tanstack": "^1.0.3", "uuid": "^11.1.0", "ws": "^8.18.0", "zod": "^3.23.8", diff --git a/src/app/chat/StandaloneChat.tsx b/src/app/chat/StandaloneChat.tsx index 54d40ba4..8198dfd7 100644 --- a/src/app/chat/StandaloneChat.tsx +++ b/src/app/chat/StandaloneChat.tsx @@ -2,11 +2,9 @@ import { memo, useEffect, useState } from 'react'; import { useSearchParams } from 'next/navigation'; -import { useSessionStore } from '@/stores/SessionStore'; import { useUser } from '@auth0/nextjs-auth0/client'; import { OpenAIMessage } from '@/lib/types'; import { - getHostSessionById, increaseSessionsCount, updateUserSession, } from '@/lib/db'; @@ -14,6 +12,7 @@ import { LoadingOverlay } from '@/components/chat/LoadingOverlay'; import { SessionModal } from '@/components/chat/SessionModal'; import { ChatInterface } from '@/components/chat/ChatInterface'; import { QuestionInfo } from 'app/create/types'; +import { useHostSession } from '@/stores/SessionStore'; const StandaloneChat = () => { const [message, setMessage] = useState({ @@ -27,11 +26,7 @@ Please type your name or "anonymous" if you prefer const searchParams = useSearchParams(); const sessionId = searchParams.get('s'); const assistantId = searchParams.get('a'); - - const [hostData, addHostData] = useSessionStore((state) => [ - sessionId ? state.hostData[sessionId] : null, - state.addHostData, - ]); + const { data: hostData } = useHostSession(sessionId || ''); const [userSessionId, setUserSessionId] = useState(() => { if (typeof window !== 'undefined' && sessionId) { @@ -90,19 +85,8 @@ Please type your name or "anonymous" if you prefer }; window.addEventListener('message', handleMessage); - - if (sessionId && !hostData) { - setIsLoading(true); - getHostSessionById(sessionId).then((data) => { - addHostData(sessionId, data); - setIsLoading(false); - }); - } else { - setIsLoading(false); - } - return () => window.removeEventListener('message', handleMessage); - }, [sessionId, hostData, addHostData]); + }, [sessionId]); useEffect(() => { if (isFirstMessage && message.role === 'assistant') { diff --git a/src/app/sessions/[id]/layout.tsx b/src/app/sessions/[id]/layout.tsx index 72e2a156..d8e9d1b6 100644 --- a/src/app/sessions/[id]/layout.tsx +++ b/src/app/sessions/[id]/layout.tsx @@ -32,8 +32,6 @@ export default function SessionLayout({ async function ProjectBar({ sessionId }: { sessionId: string }) { const projects = await getWorkspacesForSession(sessionId); - - return (
{projects.length > 0 && ( diff --git a/src/app/test-chat/page.tsx b/src/app/test-chat/page.tsx index f2f3dc97..ebe59faf 100644 --- a/src/app/test-chat/page.tsx +++ b/src/app/test-chat/page.tsx @@ -3,7 +3,7 @@ import { memo, useEffect, useState, useRef } from 'react'; import { useSearchParams } from 'next/navigation'; -import { useSessionStore } from '@/stores/SessionStore'; +import { useHostSession, useUpsertHostSession } from '@/stores/SessionStore'; import { Button } from '@/components/ui/button'; import Link from 'next/link'; import { Loader2, HelpCircle } from 'lucide-react'; @@ -33,15 +33,17 @@ Please type your name or "anonymous" if you prefer const sessionId = searchParams.get('s'); const assistantId = searchParams.get('a'); - const [hostData, addHostData] = useSessionStore((state) => [ - sessionId ? state.hostData[sessionId] : null, - state.addHostData, - ]); + const {data: hostData, isLoading: loadingHostData, error} = useHostSession(sessionId || '') + const upsertHostSession = useUpsertHostSession() + // const [hostData, addHostData] = useSessionStore((state) => [ + // sessionId ? state.hostData[sessionId] : null, + // state.addHostData, + // ]); const [userSessionId, setUserSessionId] = useState(); const [showModal, setShowModal] = useState(true); const [userFinished, setUserFinished] = useState(false); - const [isLoading, setIsLoading] = useState(true); + const [isLoading, setIsLoading] = useState(loadingHostData); const [isFirstMessage, setIsFirstMessage] = useState(true); const [isMounted, setIsMounted] = useState(false); const chatContainerRef = useRef(null); @@ -89,7 +91,6 @@ Please type your name or "anonymous" if you prefer if (sessionId && !hostData) { setIsLoading(true); getHostSessionById(sessionId).then((data) => { - addHostData(sessionId, data); setIsLoading(false); }); } else { diff --git a/src/app/workspace/[w_id]/WorkspaceContent.tsx b/src/app/workspace/[w_id]/WorkspaceContent.tsx index 883cedac..cd164d24 100644 --- a/src/app/workspace/[w_id]/WorkspaceContent.tsx +++ b/src/app/workspace/[w_id]/WorkspaceContent.tsx @@ -4,19 +4,17 @@ import ResultTabs from '@/components/SessionResult/ResultTabs'; import WorkspaceHero from '@/components/workspace/WorkspaceHero'; import ShareSettings from '@/components/ShareSettings'; import { - NewWorkspace, ResultTabsVisibilityConfig, Workspace, } from '@/lib/schema'; import { usePermissions } from '@/lib/permissions'; -import { useEffect, useState, useMemo, useCallback } from 'react'; +import { useMemo, useCallback } from 'react'; import { ExtendedWorkspaceData } from '@/lib/types'; import SessionInsightsGrid from '@/components/workspace/SessionInsightsGrid'; import { PromptSettings } from '@/components/SessionResult/ResultTabs/components/PromptSettings'; import { toast } from 'hooks/use-toast'; import * as db from '@/lib/db'; import { Loader2 } from 'lucide-react'; -import { useSessionStore } from '@/stores/SessionStore'; // Default visibility configuration for workspaces const defaultWorkspaceVisibilityConfig: ResultTabsVisibilityConfig = { @@ -38,32 +36,6 @@ export default function WorkspaceContent({ extendedWorkspaceData, workspaceId, }: WorkspaceContentProps) { - // Prime the store with initial data if needed (SSR hydration) - const { - workspaceMapping, - workspaceData, - addHostData, - addUserData, - addWorkspaceData, - } = useSessionStore((state) => ({ - ...state, - workspaceMapping: state.workspaceMapping[workspaceId], - workspaceData: state.workspaceData[workspaceId] - })); - - // Hydrate store on mount if not already present - useEffect(() => { - if (!workspaceMapping) { - addWorkspaceData(workspaceId, extendedWorkspaceData.workspace,extendedWorkspaceData.hostSessions.map(hs => hs.id)); - extendedWorkspaceData.hostSessions.forEach(hostSession => { - addHostData(hostSession.id, hostSession); - addUserData(hostSession.id, extendedWorkspaceData.userData.filter(user => user.session_id === hostSession.id)); - }); - } - }, [addHostData, addUserData, addWorkspaceData, extendedWorkspaceData, workspaceId, workspaceMapping]); - - // We still need the 'extendedData' object as separate var to keep track of linked sessions & exist status etc... - const [extendedData, setExtendedData] = useState(extendedWorkspaceData); const { hasMinimumRole, @@ -88,7 +60,7 @@ export default function WorkspaceContent({ [isPublic], ); - const exists = extendedData.exists; + const exists = extendedWorkspaceData.exists; // Memoize permission checks const isEditable = useMemo( @@ -134,40 +106,19 @@ export default function WorkspaceContent({ [workspaceId], ); - const handlePromptChangeSession = async ( - newPrompt: string, - type: 'facilitation' | 'summary', - ) => { - try { - const updateData = - type === 'facilitation' - ? { prompt: newPrompt } - : { summary_prompt: newPrompt }; - - await db.updateHostSession(id, updateData); - } catch (error) { - console.error('Failed to update prompt:', error); - toast({ - title: 'Failed to update prompt', - description: 'An error occurred while updating the prompt.', - variant: 'destructive', - }); - } - }; - // Memoize the chat entry message const chatEntryMessage = useMemo( () => ({ role: 'assistant' as const, content: `Welcome to ${ - workspaceData?.title || 'this project' + extendedWorkspaceData.workspace.title || 'this project' }! I'm here to help you understand the learnings across the linked discussions. Here are some questions you might want to ask: - What were the main themes discussed during the sessions? - What was controversial, and where did participants agree?`, }), - [workspaceData?.title], + [extendedWorkspaceData], ); if (loadingUserInfo) { @@ -187,13 +138,13 @@ Here are some questions you might want to ask: @@ -201,7 +152,7 @@ Here are some questions you might want to ask:
@@ -214,19 +165,17 @@ Here are some questions you might want to ask: resourceId={workspaceId} isWorkspace={true} visibilityConfig={ - workspaceData?.visibility_settings || visibilityConfig + extendedWorkspaceData.workspace?.visibility_settings || visibilityConfig } - sessionIds={extendedData.sessionIds} + sessionIds={extendedWorkspaceData.sessionIds} chatEntryMessage={chatEntryMessage} showEdit={canEdit} draft={!exists} >
diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index 35d145c6..563a372c 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -7,7 +7,6 @@ import { OpenAIMessage } from '@/lib/types'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { SessionStatus } from '@/lib/clientUtils'; import { useEffect } from 'react'; -import { useSessionStore } from '@/stores/SessionStore'; interface SessionPageProps { data: SessionData; @@ -23,13 +22,6 @@ export default function SessionPage({ chatEntryMessage, }: SessionPageProps) { const { hostData, usersWithChat, stats } = data; - const { addHostData, addUserData } = useSessionStore() - - useEffect(() => { - console.log("Adding data to the store: ", hostData.id) - addHostData(hostData.id, hostData); - addUserData(hostData.id, data.userData); - }, [addHostData, addUserData, hostData, data]) const status = !hostData.active || hostData.final_report_sent diff --git a/src/components/SessionResult/ParticipantSessionRow.tsx b/src/components/SessionResult/ParticipantSessionRow.tsx index d1b35edf..06218acf 100644 --- a/src/components/SessionResult/ParticipantSessionRow.tsx +++ b/src/components/SessionResult/ParticipantSessionRow.tsx @@ -10,7 +10,7 @@ import { Spinner } from '../icons'; import { Switch } from '../ui/switch'; import { MessageSquare, Star, X } from 'lucide-react'; import * as db from '@/lib/db'; -import { useSessionStore } from '@/stores/SessionStore'; +import { useUpsertUserSessions } from '@/stores/SessionStore'; const EMOJI_RATINGS = [ { @@ -49,18 +49,15 @@ export default function ParicipantSessionRow({ const [isPopupVisible, setIsPopupVisible] = useState(false); const [messages, setMessages] = useState([]); const [rating, setRating] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const { updateUserData } = useSessionStore(); - + const [isLoading, setIsLoading] = useState(false); + const updateUserSession = useUpsertUserSessions(); const handleIncludeInSummaryUpdate = async (included: boolean) => { - // Update the store immediately for optimistic update - updateUserData(userData.session_id, userData.id, { include_in_summary: included }); - - // Update the field in the db - await db.updateUserSession(userData.id, { + // Updates the store & db + updateUserSession.mutate({ + ...userData, include_in_summary: included, - last_edit: new Date(), + last_edit: new Date() }); }; diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 6e55b7d8..1d21a703 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -28,7 +28,7 @@ import { } from '@/components/ui/resizable'; import { SimScoreTab } from './SimScoreTab'; import SessionFilesTable from '../SessionFilesTable'; -import { useSessionStore } from '@/stores/SessionStore'; +import { useHostSession, useUserSessions, useWorkspace } from '@/stores/SessionStore'; import { SummaryUpdateManager } from '../../../summary/SummaryUpdateManager'; export interface ResultTabsProps { @@ -70,10 +70,19 @@ export default function ResultTabs({ const { responses, addResponse, removeResponse } = useCustomResponses(resourceId); - const { hostData, userData: currentUserData } = useSessionStore((state) => ({ - hostData: Object.values(state.hostData), - userData: state.userData, - })); + let hostData, userData + if (isProject) { + const { data: workspaceData, isLoading: isLoadingWspace } = useWorkspace(resourceId); + hostData = workspaceData?.hostSessions || [] + userData = workspaceData?.userData || [] + } else { + const { data: hData } = useHostSession(resourceId); + hostData = hData ? [hData] : [] + const { data: uData } = useUserSessions(resourceId); + userData = uData ? uData : [] + } + + const currentUserData = userData; useEffect(() => { SummaryUpdateManager.startPolling(resourceId, 10000); // Poll every 10 seconds diff --git a/src/components/SessionResult/SessionResultHeader.tsx b/src/components/SessionResult/SessionResultHeader.tsx index 90c832b1..190c3617 100644 --- a/src/components/SessionResult/SessionResultHeader.tsx +++ b/src/components/SessionResult/SessionResultHeader.tsx @@ -4,9 +4,10 @@ import { Check, X } from 'lucide-react'; import { useState, useRef, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { updateHostSession } from '@/lib/db'; -import { useSessionStore } from '@/stores/SessionStore'; +import { useHostSession, useUpsertHostSession } from '@/stores/SessionStore'; import { usePermissions } from '@/lib/permissions'; import { SessionStatus } from '@/lib/clientUtils'; +import { NewHostSession } from '@/lib/schema'; interface SessionResultHeaderProps { sessionId: string; @@ -21,7 +22,8 @@ export default function SessionResultHeader({ }: SessionResultHeaderProps) { const [isEditing, setIsEditing] = useState(false); const editableRef = useRef(null); - const { addHostData } = useSessionStore(); + const { data: hostData } = useHostSession(sessionId); + const upsertHostSession = useUpsertHostSession(); const { hasMinimumRole } = usePermissions(sessionId); const isEditable = hasMinimumRole('editor'); @@ -47,7 +49,7 @@ export default function SessionResultHeader({ await updateHostSession(sessionId, { topic: content }); // Update the local state in the SessionStore - addHostData(sessionId, { id: sessionId, topic: content } as any); + upsertHostSession.mutate({ id: sessionId, topic: content } as NewHostSession); setIsEditing(false); } catch (error) { diff --git a/src/components/workspace/SessionInsightsGrid.tsx b/src/components/workspace/SessionInsightsGrid.tsx index 22d22019..0834b2c9 100644 --- a/src/components/workspace/SessionInsightsGrid.tsx +++ b/src/components/workspace/SessionInsightsGrid.tsx @@ -2,7 +2,7 @@ import { Card, CardContent, CardHeader } from '@/components/ui/card'; import SessionSummaryCard from '@/components/SessionResult/SessionSummaryCard'; import { HostSession, UserSession } from '@/lib/schema'; import { Button } from '@/components/ui/button'; -import { LinkIcon, Pencil, Plus } from 'lucide-react'; +import { LinkIcon, Loader, Pencil, Plus } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { useEffect, useState, useCallback } from 'react'; import { @@ -21,43 +21,41 @@ import { } from '@/lib/workspaceActions'; import { useToast } from 'hooks/use-toast'; import * as db from '@/lib/db'; -import { useSessionStore } from '@/stores/SessionStore'; +import { + useUpsertWorkspace, + useUpsertHostSession, + useUpsertUserSessions, + useHostSession, + useWorkspace, + useLinkSessionsToWorkspace, + useUnlinkSessionFromWorkspace, +} from '@/stores/SessionStore'; interface SessionInsightsGridProps { - hostSessions: HostSession[]; - userData: UserSession[]; workspaceId: string; showEdit?: boolean; availableSessions?: Pick[]; } export default function SessionInsightsGrid({ - hostSessions, - userData, workspaceId, showEdit = false, availableSessions = [], }: SessionInsightsGridProps) { const router = useRouter(); const { toast } = useToast(); + const { data: projectData, isLoading, isError } = useWorkspace(workspaceId); + const localHostSessions = projectData?.hostSessions ?? []; + const localUserData = projectData?.userData ?? []; + const [selectedSessions, setSelectedSessions] = useState([]); const [isLinking, setIsLinking] = useState(false); - const [localHostSessions, setLocalHostSessions] = - useState(hostSessions); - const [localUserData, setLocalUserData] = useState(userData); const [dialogOpen, setDialogOpen] = useState(false); - const { upsertWorkspaces, upsertHostData, upsertUserData, hostData, userData: storeUserData } = useSessionStore() - - // Update local state when props change - useEffect(() => { - setLocalHostSessions(hostSessions); - setLocalUserData(userData); - }, [hostSessions, userData]); // Filter out sessions that are already in the workspace const sessionsToLink = availableSessions.filter( (session) => - !localHostSessions.some((hostSession) => hostSession.id === session.id), + !localHostSessions.some((hostSession) => hostSession.id === session.id) ); const handleCreateSession = () => { @@ -68,105 +66,39 @@ export default function SessionInsightsGrid({ setSelectedSessions((prev) => prev.includes(sessionId) ? prev.filter((id) => id !== sessionId) - : [...prev, sessionId], + : [...prev, sessionId] ); }; + const linkSessions = useLinkSessionsToWorkspace(); const handleLinkSessions = async () => { - if (selectedSessions.length === 0) return; - - setIsLinking(true); - try { - // First, link the sessions to the workspace - await linkSessionsToWorkspace(workspaceId, selectedSessions); - - // Determine which sessions are not already in the store - const sessionsToFetch = selectedSessions.filter( - (sessionId) => !hostData[sessionId] - ); - - let newSessionsData: HostSession[] = []; - let newUserData: UserSession[][] = []; - - if (sessionsToFetch.length > 0) { - // Fetch only missing session data and user data - [newSessionsData, newUserData] = await Promise.all([ - Promise.all( - sessionsToFetch.map(async (sessionId) => { - const sessionData = await db.getHostSessionById(sessionId); - return sessionData; - }) - ), - Promise.all( - sessionsToFetch.map(async (sessionId) => { - const users = await db.getUsersBySessionId(sessionId); - return users; - }) - ), - ]); - - // Update local state with the new session data - setLocalHostSessions((prev) => [...prev, ...newSessionsData]); - - // Update local user data - const flattenedUserData = newUserData.flat(); - setLocalUserData((prev) => [...prev, ...flattenedUserData]); - - // Update the store with new data - newSessionsData.forEach((host, idx) => { - upsertHostData(host.id, host); - upsertUserData(host.id, newUserData[idx]); - }); - } + linkSessions.mutate({ workspaceId, sessionIds: selectedSessions }); + if (linkSessions.isPending) return ; + if (linkSessions.isError) + toast({ title: 'Error', description: linkSessions.error.message }); + if (linkSessions.isSuccess) + toast({ title: 'Success', description: 'Sessions linked!' }); + }; - // Always update workspace mapping in the store - upsertWorkspaces(workspaceId, {}, selectedSessions); + const unlinkSession = useUnlinkSessionFromWorkspace(); + const handleRemoveSession = async (sessionId: string) => { + unlinkSession.mutate({ workspaceId, sessionId }); - setDialogOpen(false); - toast({ - title: 'Success', - description: 'Sessions linked successfully', - }); - } catch (error) { - console.error('Failed to link sessions:', error); + if (unlinkSession.isPending) return ; + if (unlinkSession.isError) { toast({ title: 'Error', - description: 'Failed to link sessions to project', + description: + 'Failed to remove session from project\n' + + unlinkSession.error.message, variant: 'destructive', }); - } finally { - setIsLinking(false); - setSelectedSessions([]); } - }; - - const handleRemoveSession = async (sessionId: string) => { - try { - // Update local state immediately - setLocalHostSessions((prev) => - prev.filter((session) => session.id !== sessionId), - ); - - // Perform the actual unlinking - await unlinkSessionFromWorkspace(workspaceId, sessionId); - - // Update links in the store (but don't remove fetched sessions, it won't be visible but available faster if the user visits it again...) - upsertWorkspaces(workspaceId, {}, [...selectedSessions.filter(session => session !== sessionId)]); - + if (unlinkSession.isSuccess) { toast({ title: 'Session removed', description: 'The session has been removed from this project', }); - } catch (error) { - console.error('Failed to remove session:', error); - toast({ - title: 'Error', - description: 'Failed to remove session from project', - variant: 'destructive', - }); - - // Revert the local state change if the operation failed - setLocalHostSessions(hostSessions); } }; @@ -181,7 +113,7 @@ export default function SessionInsightsGrid({ {localHostSessions.map((hostData) => { // Get the corresponding user data for this session const sessionUserData = localUserData.filter( - (user) => user.session_id === hostData.id, + (user) => user.session_id === hostData.id ); // Calculate the number of participants @@ -273,7 +205,7 @@ export default function SessionInsightsGrid({

{new Date( - session.start_time, + session.start_time ).toLocaleDateString()}

diff --git a/src/lib/db.ts b/src/lib/db.ts index 58355196..c3f63c15 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -187,7 +187,7 @@ export async function insertHostSessions( export async function upsertHostSession( data: s.NewHostSession, onConflict: 'skip' | 'update' = 'skip', -): Promise { +): Promise { console.log('[i] Database Operation: upsertHostSession'); const db = await dbPromise; try { @@ -195,7 +195,7 @@ export async function upsertHostSession( // But, we kind of have to, otherwise we wouldn't be able to have 'private' sessions at all and all editing etc would be public :-/ const session = await authGetSession(); const userSub = session?.user?.sub || ''; - await db + const result = await db .insertInto(hostTableName) .values({ ...data, client: userSub }) .onConflict((oc) => @@ -203,7 +203,9 @@ export async function upsertHostSession( ? oc.column('id').doNothing() : oc.column('id').doUpdateSet(data), ) - .execute(); + .returningAll() + .executeTakeFirstOrThrow(); + return result; } catch (error) { console.error('Error upserting host session:', error); throw error; @@ -297,6 +299,30 @@ export async function insertUserSessions( } } +export async function upsertUserSessions( + data: s.NewUserSession | s.NewUserSession[], + onConflict: 'skip' | 'update' = 'update', +): Promise { + console.log('[i] Database Operation: upsertUserSessions'); + try { + const db = await dbPromise; + const result = await db + .insertInto(userTableName) + .values(data) + .onConflict((oc) => + onConflict === 'skip' + ? oc.column('id').doNothing() + : oc.column('id').doUpdateSet(data), + ) + .returningAll() + .execute(); + return result; + } catch (error) { + console.error('Error upserting user session:', error); + throw error; + } +} + export async function updateUserSession( id: string, data: s.UserSessionUpdate, diff --git a/src/lib/workspaceActions.ts b/src/lib/workspaceActions.ts index 0940a164..8ddd0fce 100644 --- a/src/lib/workspaceActions.ts +++ b/src/lib/workspaceActions.ts @@ -2,6 +2,8 @@ import * as db from '@/lib/db'; +// TODO: we could use the tanstack store to wrap & cache this as well. Not sure whether it's necessary though. + /** * Links one or more sessions to a workspace */ diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index c4c5ff25..a68bf88e 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,64 +1,132 @@ -import { create } from 'zustand' -import { HostSession, Message, NewWorkspace, UserSession, Workspace } from '@/lib/schema'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import * as db from '@/lib/db'; +import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace } from '@/lib/schema'; +import { fetchWorkspaceData } from '@/lib/workspaceData'; +import { linkSessionsToWorkspace, unlinkSessionFromWorkspace } from '@/lib/workspaceActions'; -interface SessionStore { - workspaceData: Record - workspaceMapping: Record // workspaceId to session IDs - upsertWorkspaceData: (workspaceId: string, workspaceData: Workspace | NewWorkspace, hostSessionIds: string[]) => void - upsertWorkspaces: (workspaceId: string, updates: Partial, hostSessionIds: string[]) => void - hostData: Record - upsertHostData: (id: string, data: HostSession) => void - userData: Record - upsertUserData: (sessionId: string, data: UserSession[]) => void - messageData: Record - upsertMessageData: (id: string, data: Message[]) => void - removeSession: (id: string) => void + +// --- Query Keys --- +const workspaceKey = (id: string) => ['workspace', id]; +const hostKey = (id: string) => ['host', id]; +const userKey = (sessionId: string) => ['users', sessionId]; +const messageKey = (threadId: string) => ['messages', threadId]; +const staleTime = 1000 * 60; // 1 minute + +// --- Fetchers --- +export function useWorkspace(workspaceId: string) { + return useQuery({ + queryKey: workspaceKey(workspaceId), + queryFn: () => fetchWorkspaceData(workspaceId), + select: (data) => data ?? [], // Returns an empty array if data isn't available yet + enabled: !!workspaceId, + staleTime, + }); +} + +export function useHostSession(sessionId: string) { + return useQuery({ + queryKey: hostKey(sessionId), + queryFn: () => db.getHostSessionById(sessionId), + select: (data) => data ?? [], + enabled: !!sessionId, + staleTime, + }); +} + +export function useUserSessions(sessionId: string) { + return useQuery({ + queryKey: userKey(sessionId), + queryFn: () => db.getUsersBySessionId(sessionId), + select: (data) => data ?? [], + enabled: !!sessionId, + staleTime, + }); +} + +export function useMessages(threadId: string) { + return useQuery({ + queryKey: messageKey(threadId), + queryFn: () => db.getAllChatMessagesInOrder(threadId), + enabled: !!threadId, + }); +} + +// --- Mutations --- +export function useUpsertWorkspace() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: Partial }) => + db.upsertWorkspace(id, data), + onSuccess: (_data, variables) => { + queryClient.invalidateQueries({ queryKey: workspaceKey(variables.id) }); + }, + }); +} + +export function useUpsertHostSession() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (data: NewHostSession) => db.upsertHostSession(data, 'update'), + onSuccess: (result, _data) => { + queryClient.invalidateQueries({ queryKey: hostKey(result.id) }); + }, + }); +} + +export function useUpsertUserSessions() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (data: NewUserSession | NewUserSession[]) => db.upsertUserSessions(data), + onSuccess: (result, _input) => { + // Invalidate all affected session queries so that if we query for a session it will be fetched + const sessionIds = result.map(v => v.session_id); + sessionIds.forEach(sessionId => { + queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); + }); + }, + }); +} + +export function useUpsertMessages() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (message: NewMessage) => db.insertChatMessage(message), + onSuccess: (_result, input) => { + queryClient.invalidateQueries({ queryKey: messageKey(input.thread_id) }); + }, + }); +} + +export function useRemoveSession() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (sessionId: string) => db.deleteSessionById(sessionId), + onSuccess: (_data, sessionId) => { + queryClient.invalidateQueries({ queryKey: hostKey(sessionId) }); + queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); + // Optionally, invalidate workspace/session lists if needed + }, + }); +} + +export function useLinkSessionsToWorkspace() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ workspaceId, sessionIds }: { workspaceId: string; sessionIds: string[] }) => + await linkSessionsToWorkspace(workspaceId, sessionIds), + onSuccess: (_result, variables) => { + queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); + }, + }); } -export const useSessionStore = create((set) => ({ - workspaceData: {}, - workspaceMapping: {}, - upsertWorkspaceData: (workspaceId, workspaceData, hostSessionIds) => set((state) => ({ - workspaceData: { ...state.workspaceData, [workspaceId]: workspaceData }, - workspaceMapping: { ...state.workspaceMapping, [workspaceId]: hostSessionIds } - })), - upsertWorkspaces: (workspaceId, updates, hostSessionIds) => set((state) => ({ - workspaceData: { - ...state.workspaceData, - [workspaceId]: { - ...state.workspaceData[workspaceId], - ...updates - } +export function useUnlinkSessionFromWorkspace() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ workspaceId, sessionId }: { workspaceId: string; sessionId: string }) => + await unlinkSessionFromWorkspace(workspaceId, sessionId), + onSuccess: (_result, variables) => { + queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); }, - workspaceMapping: { ...state.workspaceMapping, [workspaceId]: hostSessionIds } - })), - hostData: {}, - upsertHostData: (sessionId, data) => set((state) => ({ - hostData: { ...state.hostData, [sessionId]: data } - })), - userData: {}, - upsertUserData: (sessionId, data) => set((state) => { - const existingUsers = state.userData[sessionId] || []; - // Create a map for quick lookup - const existingMap = new Map(existingUsers.map(user => [user.id, user])); - // Merge: update existing, add new - data.forEach(user => { - existingMap.set(user.id, { ...existingMap.get(user.id), ...user }); - }); - return { - userData: { - ...state.userData, - [sessionId]: Array.from(existingMap.values()), - } - }; - }), - messageData: {}, - upsertMessageData: (threadId, data) => set((state) => ({ - messageData: { ...state.messageData, [threadId]: data } - })), - removeSession: (sessionId) => set((state) => { - const { [sessionId]: removedHostData, ...remainingHostData } = state.hostData; - const { [sessionId]: removedUserData, ...remainingUserData } = state.userData; - return { hostData: remainingHostData, userData: remainingUserData }; - }) -})) \ No newline at end of file + }); +} \ No newline at end of file From 558365036eca116794f2c758cb98c2543dfa95f2 Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 28 Jul 2025 11:01:36 +1200 Subject: [PATCH 06/27] Fix Provider --- src/app/providers.tsx | 13 +++++++---- .../SessionResult/ExpandableWithExport.tsx | 1 - .../SessionResult/ResultTabs/index.tsx | 22 +++++++++++-------- src/summary/SummaryUpdateManager.ts | 11 ++++++---- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/app/providers.tsx b/src/app/providers.tsx index a8f837aa..281437df 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -6,6 +6,7 @@ import { PostHogProvider } from 'posthog-js/react'; import { useEffect } from 'react'; import { useToast } from 'hooks/use-toast'; import { processUserInvitations } from './actions/process-invitations'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; if (typeof window !== 'undefined') { posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { @@ -16,6 +17,8 @@ if (typeof window !== 'undefined') { }); } +const queryClient = new QueryClient(); + // Component to handle automatic invitation processing function InvitationProcessor() { const { user, isLoading } = useUser(); @@ -54,10 +57,12 @@ function InvitationProcessor() { export function Providers({ children }: { children: React.ReactNode }) { return ( - - - {children} - + + + + {children} + + ); } diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index 326f6712..95ef6e05 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -62,7 +62,6 @@ export const ExpandableWithExport = ({ // Only update if status actually changes SummaryUpdateManager.subscribe(resourceId, (state) => { - console.log("[ExpandableComponent]: Updating status: ", state.status); refreshStatusRef.current = state.status; }); return ( diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 1d21a703..cb9828d8 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -14,7 +14,7 @@ import { UserSession, } from '@/lib/schema'; import { OpenAIMessage } from '@/lib/types'; -import { CirclePlusIcon } from 'lucide-react'; +import { CirclePlusIcon, Loader, Loader2 } from 'lucide-react'; import { CustomResponseCard } from './components/CustomResponseCard'; import { TabContent } from './components/TabContent'; import { useCustomResponses } from './hooks/useCustomResponses'; @@ -70,16 +70,18 @@ export default function ResultTabs({ const { responses, addResponse, removeResponse } = useCustomResponses(resourceId); - let hostData, userData + let hostData, userData, isLoadingData if (isProject) { - const { data: workspaceData, isLoading: isLoadingWspace } = useWorkspace(resourceId); + const { data: workspaceData, isLoading } = useWorkspace(resourceId); + isLoadingData = isLoading hostData = workspaceData?.hostSessions || [] userData = workspaceData?.userData || [] } else { - const { data: hData } = useHostSession(resourceId); + const { data: hData, isLoading } = useHostSession(resourceId); hostData = hData ? [hData] : [] const { data: uData } = useUserSessions(resourceId); userData = uData ? uData : [] + isLoadingData = isLoading } const currentUserData = userData; @@ -304,17 +306,19 @@ export default function ResultTabs({ // If all tabs are disabled, or no replies are available yet, show some empty message. // (For new projects, we'll show a placeholder instead of this) - if ( - visibleTabs.length === 0 || - (!draft && !hasMinimumRole('editor')) + if (isLoadingData || + (visibleTabs.length === 0 || + (!draft && !hasMinimumRole('editor'))) ) { return ( - Nothing to see yet ¯\_(ツ)_/¯ +
+ Loading data... +
- ); + ) } return ( diff --git a/src/summary/SummaryUpdateManager.ts b/src/summary/SummaryUpdateManager.ts index e95276e9..6b38d806 100644 --- a/src/summary/SummaryUpdateManager.ts +++ b/src/summary/SummaryUpdateManager.ts @@ -50,6 +50,7 @@ class SummaryUpdateManagerClass { if (state?.pollingIntervalId) { clearInterval(state.pollingIntervalId); state.pollingIntervalId = undefined; + console.log("Stopped polling for", resourceId); } } @@ -80,7 +81,6 @@ class SummaryUpdateManagerClass { state.lastEditTimestamp = updateTimes.lastEdit; } } else { - console.log(`[SummaryUpdateManager] No update needed for ${resourceId}`); state.status = RefreshStatus.UpToDate; } } catch (error) { @@ -95,9 +95,12 @@ class SummaryUpdateManagerClass { return () => this.getOrCreateState(resourceId).listeners.delete(listener); } - private notify(resourceId: string) { + private notify(resourceId: string, message?: string) { const state = this.updates.get(resourceId); if (state) { + if (message) { + console.log(`[SummaryUpdateManager] Notifying listeners for ${resourceId}: ${message}`); + } state.listeners.forEach((fn) => fn(state)); } } @@ -112,7 +115,7 @@ class SummaryUpdateManagerClass { return; } state.isRunning = true; - this.notify(resourceId); + this.notify(resourceId, "Update started"); try { console.log(`[SummaryUpdateManager] Executing update for ${resourceId}`, opts); @@ -143,7 +146,7 @@ class SummaryUpdateManagerClass { // Clean up state state.isRunning = false; state.status = RefreshStatus.UpToDate; - this.notify(resourceId); + this.notify(resourceId, "Update finished"); } } From b21842173bafa2b5f0c363dae03e8c1e5ff67246 Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 28 Jul 2025 12:04:40 +1200 Subject: [PATCH 07/27] Claude Tanstacking --- CLAUDE.md | 5 +- src/app/providers.tsx | 30 ++- src/app/sessions/[id]/page.tsx | 51 ++++- src/app/workspace/[w_id]/page.tsx | 84 ++++++-- .../SessionResult/ExpandableWithExport.tsx | 22 +- .../SessionResult/ResultTabs/index.tsx | 14 +- .../SessionResult/SessionResultControls.tsx | 10 +- .../SessionResult/SessionResultHeader.tsx | 6 +- .../SessionResult/SessionResultSummary.tsx | 17 +- src/hooks/useSummaryUpdateManager.ts | 157 ++++++++++++++ src/lib/summaryActions.ts | 75 ++++++- src/lib/workspaceData.ts | 86 +++++--- src/stores/SessionStore.ts | 192 +++++++++++++++++- src/summary/SummaryUpdateManager.ts | 164 --------------- 14 files changed, 665 insertions(+), 248 deletions(-) create mode 100644 src/hooks/useSummaryUpdateManager.ts delete mode 100644 src/summary/SummaryUpdateManager.ts diff --git a/CLAUDE.md b/CLAUDE.md index cb0fd557..c00e0b6d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,12 +28,11 @@ You are an expert full-stack developer proficient in TypeScript, React, Next.js, - Use custom error types for consistent error handling. ## UI and Styling -- Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling. +- Use modern UI frameworks (Tailwind CSS, Shadcn UI, Radix UI) for styling. - Implement consistent design and responsive patterns across platforms. ## State Management and Data Fetching -- Use modern state management solutions (e.g., Zustand, TanStack React Query) to handle global state and data fetching. -- Implement validation using Zod for schema validation. +- Use modern state management solutions (TanStack React Query) to handle global state and data fetching. ## Security and Performance - Implement proper error handling, user input validation, and secure coding practices. diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 281437df..4c919764 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -6,7 +6,7 @@ import { PostHogProvider } from 'posthog-js/react'; import { useEffect } from 'react'; import { useToast } from 'hooks/use-toast'; import { processUserInvitations } from './actions/process-invitations'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query'; if (typeof window !== 'undefined') { posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { @@ -17,7 +17,32 @@ if (typeof window !== 'undefined') { }); } -const queryClient = new QueryClient(); +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // set some default staleTime above 0 to avoid refetching immediately on the client + staleTime: 60 * 1000, + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +function getQueryClient() { + if (isServer) { + // Server: always make a new query client + return makeQueryClient() + } else { + // Browser: reuse the existing query client if possible + // This is very important, so we don't re-make a new client if React + // suspends during the initial render. This may not be needed if we + // have a suspense boundary BELOW the creation of the query client + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} // Component to handle automatic invitation processing function InvitationProcessor() { @@ -55,6 +80,7 @@ function InvitationProcessor() { } export function Providers({ children }: { children: React.ReactNode }) { + const queryClient = getQueryClient(); return ( diff --git a/src/app/sessions/[id]/page.tsx b/src/app/sessions/[id]/page.tsx index 18d0543c..762211b8 100644 --- a/src/app/sessions/[id]/page.tsx +++ b/src/app/sessions/[id]/page.tsx @@ -3,7 +3,9 @@ import { getGeneratedMetadata } from 'app/api/metadata'; import { decryptId } from '@/lib/encryptionUtils'; import SessionDataProvider from '@/components/SessionPage/SessionDataProvider'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; -import { access } from 'fs'; +import { QueryClient, HydrationBoundary, dehydrate } from '@tanstack/react-query'; +import * as db from '@/lib/db'; +import { checkSummaryNeedsUpdating, fetchSummary } from '@/lib/summaryActions'; // Increase the maximum execution time for this function on vercel export const maxDuration = 60; // in seconds @@ -21,6 +23,41 @@ export default async function SessionResult({ params: { id: string }; }) { const decryptedId = decryptId(params.id); + const queryClient = new QueryClient(); + + // Prefetch all session-related data for optimal performance + // (This is a server component, data is being prefetched on the server and then dehydrated, passed to the client and then further updates will happen there) + // TanStack is taking care of the hydration magic. + try { + await Promise.allSettled([ + // Prefetch host session data + queryClient.prefetchQuery({ + queryKey: ['host', decryptedId], + queryFn: () => db.getHostSessionById(decryptedId), + }), + + // Prefetch user sessions + queryClient.prefetchQuery({ + queryKey: ['users', decryptedId], + queryFn: () => db.getUsersBySessionId(decryptedId), + }), + + // Prefetch summary status (for summary update manager) + queryClient.prefetchQuery({ + queryKey: ['summary-status', decryptedId], + queryFn: () => checkSummaryNeedsUpdating(decryptedId), + }), + + // Prefetch summary content + queryClient.prefetchQuery({ + queryKey: ['summary-content', decryptedId], + queryFn: () => fetchSummary(decryptedId), + }), + ]); + } catch (error) { + // If prefetching fails, continue rendering - the client will fetch data as needed + console.warn('Failed to prefetch session data:', error); + } const visibilityConfig: ResultTabsVisibilityConfig = { showSummary: true, @@ -31,10 +68,12 @@ export default async function SessionResult({ }; return ( - + + + ); } \ No newline at end of file diff --git a/src/app/workspace/[w_id]/page.tsx b/src/app/workspace/[w_id]/page.tsx index 556a57ed..0142ee4a 100644 --- a/src/app/workspace/[w_id]/page.tsx +++ b/src/app/workspace/[w_id]/page.tsx @@ -4,16 +4,14 @@ import WorkspaceContent from './WorkspaceContent'; import ErrorPage from '@/components/Error'; import { fetchWorkspaceData } from '@/lib/workspaceData'; import { ExtendedWorkspaceData } from '@/lib/types'; -import { cache } from 'react'; +import { QueryClient, dehydrate, HydrationBoundary } from '@tanstack/react-query'; +import * as db from '@/lib/db'; +import { getSession } from '@auth0/nextjs-auth0'; // Increase the maximum execution time for this function on vercel export const maxDuration = 300; // in seconds export const revalidate = 0; // Disable caching for this page -// Create a cached version of fetchWorkspaceData -const cachedFetchWorkspaceData = cache(async (workspaceId: string): Promise => { - return fetchWorkspaceData(workspaceId); -}); export async function generateMetadata({ params, @@ -28,17 +26,79 @@ export default async function Workspace({ }: { params: { w_id: string }; }) { + const queryClient = new QueryClient(); try { - const data: ExtendedWorkspaceData = await cachedFetchWorkspaceData(params.w_id); + // First prefetch workspace data + await queryClient.prefetchQuery({ + queryKey: ['workspace', params.w_id], + queryFn: () => fetchWorkspaceData(params.w_id, queryClient) + }); + + // Get the cached workspace data to access session IDs for further prefetching + const data: ExtendedWorkspaceData = queryClient.getQueryData(['workspace', params.w_id])!; + + // Only prefetch individual session data if workspace exists and has sessions + if (data.exists && data.sessionIds.length > 0) { + console.log(`Prefetching data for ${data.sessionIds.length} sessions...`); + + await Promise.allSettled([ + // Prefetch all individual session data for cache reuse + ...data.sessionIds.map(id => + queryClient.prefetchQuery({ + queryKey: ['host', id], + queryFn: () => db.getHostSessionById(id) + }) + ), + ...data.sessionIds.map(id => + queryClient.prefetchQuery({ + queryKey: ['users', id], + queryFn: () => db.getUsersBySessionId(id) + }) + ), + + // Prefetch workspace-specific data + queryClient.prefetchQuery({ + queryKey: ['workspace-sessions', params.w_id], + queryFn: () => db.getWorkspaceSessionIds(params.w_id) + }), + + queryClient.prefetchQuery({ + queryKey: ['workspace-stats', data.sessionIds.sort()], + queryFn: () => db.getNumUsersAndMessages(data.sessionIds) + }), + + // Prefetch available sessions + queryClient.prefetchQuery({ + queryKey: ['available-sessions'], + queryFn: async () => { + const session = await getSession(); + const userId = session?.user?.sub; + const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); + const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); + + if (availableSessionsIds.length > 0) { + return await db.getHostSessionsForIds(availableSessionsIds, [ + 'id', 'topic', 'start_time' + ]); + } + return []; + } + }) + ]); + + console.log('Prefetching completed successfully'); + } return ( -
- -
+ +
+ +
+
); } catch (error) { console.error(`Error occurred fetching data: `, error); diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index 95ef6e05..8f1b829b 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -10,8 +10,7 @@ import { import ExpandableCard from '../ui/expandable-card'; import { ExportButton } from '../Export/ExportButton'; import { Spinner } from '../icons'; -import { RefreshStatus, SummaryUpdateManager } from 'summary/SummaryUpdateManager'; -import { useRef } from 'react'; +import { RefreshStatus, useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; interface CardProps { resourceId: string; @@ -58,12 +57,7 @@ export const ExpandableWithExport = ({ loading, className, }: CardProps) => { - const refreshStatusRef = useRef(SummaryUpdateManager.getState(resourceId).status); - - // Only update if status actually changes - SummaryUpdateManager.subscribe(resourceId, (state) => { - refreshStatusRef.current = state.status; - }); + const summaryManager = useSummaryUpdateManager(resourceId); return (
- +
@@ -98,16 +92,16 @@ export const ExpandableWithExport = ({ ) : (

Refresh {title}

- {refreshStatusRef.current === RefreshStatus.Unknown && ( + {summaryManager.status === RefreshStatus.Unknown && (

Unknown update status

)} - {refreshStatusRef.current === RefreshStatus.UpToDate && ( + {summaryManager.status === RefreshStatus.UpToDate && (

Up to date

)} - {refreshStatusRef.current === RefreshStatus.UpdatePending && ( + {summaryManager.status === RefreshStatus.UpdatePending && (

Auto-refreshing soon

)} - {refreshStatusRef.current === RefreshStatus.Outdated && ( + {summaryManager.status === RefreshStatus.Outdated && (

Summary out of date

)}
diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index cb9828d8..c91033ad 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -29,7 +29,7 @@ import { import { SimScoreTab } from './SimScoreTab'; import SessionFilesTable from '../SessionFilesTable'; import { useHostSession, useUserSessions, useWorkspace } from '@/stores/SessionStore'; -import { SummaryUpdateManager } from '../../../summary/SummaryUpdateManager'; +import { useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; export interface ResultTabsProps { resourceId: string; @@ -86,10 +86,14 @@ export default function ResultTabs({ const currentUserData = userData; - useEffect(() => { - SummaryUpdateManager.startPolling(resourceId, 10000); // Poll every 10 seconds - return () => SummaryUpdateManager.stopPolling(resourceId); // Clean up on unmount - }, [resourceId]); + // Use the new TanStack Query-based summary update manager + useSummaryUpdateManager(resourceId, { + isProject, + sessionIds, + projectId: resourceId, + enablePolling: true, + pollingInterval: 10000, // Poll every 10 seconds + }); // Define available tabs and their visibility conditions in one place const availableTabs = useMemo(() => { diff --git a/src/components/SessionResult/SessionResultControls.tsx b/src/components/SessionResult/SessionResultControls.tsx index 2b717382..917d1679 100644 --- a/src/components/SessionResult/SessionResultControls.tsx +++ b/src/components/SessionResult/SessionResultControls.tsx @@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { LoaderCircle, Settings, Copy, InfoIcon, Trash2 } from 'lucide-react'; import * as db from '@/lib/db'; -import { SummaryUpdateManager } from '../../summary/SummaryUpdateManager'; +import { useUpdateSummary } from '@/stores/SessionStore'; import { cloneSession } from '@/lib/serverUtils'; import { useRouter } from 'next/navigation'; import { toast } from 'hooks/use-toast'; @@ -53,6 +53,7 @@ export default function SessionResultControls({ const [localCrossPollination, setLocalCrossPollination] = useState(crossPollination); const router = useRouter(); + const updateSummaryMutation = useUpdateSummary(); const handlePromptChange = async ( newPrompt: string, @@ -116,8 +117,11 @@ export default function SessionResultControls({ const updateSummary = async () => { setLoadSummary(true); - await SummaryUpdateManager.updateNow(id); - setLoadSummary(false); + try { + await updateSummaryMutation.mutateAsync({ sessionId: id }); + } finally { + setLoadSummary(false); + } }; const handleCloneSession = async () => { diff --git a/src/components/SessionResult/SessionResultHeader.tsx b/src/components/SessionResult/SessionResultHeader.tsx index 190c3617..7755fd47 100644 --- a/src/components/SessionResult/SessionResultHeader.tsx +++ b/src/components/SessionResult/SessionResultHeader.tsx @@ -3,7 +3,6 @@ import { Badge } from '@/components/ui/badge'; import { Check, X } from 'lucide-react'; import { useState, useRef, useEffect } from 'react'; import { Button } from '@/components/ui/button'; -import { updateHostSession } from '@/lib/db'; import { useHostSession, useUpsertHostSession } from '@/stores/SessionStore'; import { usePermissions } from '@/lib/permissions'; import { SessionStatus } from '@/lib/clientUtils'; @@ -45,10 +44,7 @@ export default function SessionResultHeader({ return; } - // Update the topic in the database - await updateHostSession(sessionId, { topic: content }); - - // Update the local state in the SessionStore + // Use TanStack Query mutation which handles both DB update and cache invalidation upsertHostSession.mutate({ id: sessionId, topic: content } as NewHostSession); setIsEditing(false); diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index ff230ad8..ba986039 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -1,7 +1,8 @@ 'use client' import { HostSession } from '@/lib/schema'; import { useEffect, useState } from 'react'; -import { RefreshStatus, SummaryUpdateManager } from '../../summary/SummaryUpdateManager'; +import { RefreshStatus } from '@/hooks/useSummaryUpdateManager'; +import { useUpdateSummary } from '@/stores/SessionStore'; import { ExpandableWithExport } from './ExpandableWithExport'; import { Card, CardContent } from '../ui/card'; import { usePermissions } from '@/lib/permissions'; @@ -30,17 +31,23 @@ export default function SessionResultSummary({ const resourceId: string = isProject ? projectId! : hostData[0].id; const { hasMinimumRole } = usePermissions(resourceId); + const updateSummaryMutation = useUpdateSummary(); // Use SWR to fetch summary content with initial data as fallback const initialSummary = isProject ? '' : hostData[0]?.summary || ''; const { data: summary, isLoading: summaryLoading } = useSummary(resourceId, initialSummary, isProject); - const manuallyTriggerSummaryUpdate = () => { + const manuallyTriggerSummaryUpdate = async () => { setIsUpdating(true); - SummaryUpdateManager.updateNow(hostData[0].id).then((_summary) => { + try { + await updateSummaryMutation.mutateAsync({ + sessionId: hostData[0].id, + isProject, + projectId + }); + } finally { setIsUpdating(false); - // Summary will be updated automatically via SWR - }); + } }; // Check which content will be shown diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts new file mode 100644 index 00000000..0b5f67ab --- /dev/null +++ b/src/hooks/useSummaryUpdateManager.ts @@ -0,0 +1,157 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useSummaryStatusOptimized, useUpdateSummary } from '@/stores/SessionStore'; + +export enum RefreshStatus { + Unknown, + Outdated, + UpdatePending, + UpToDate, +} + +interface UseUpdateManagerOptions { + isProject?: boolean; + sessionIds?: string[]; + projectId?: string; + source?: 'participants' | 'chat' | 'ui' | 'auto'; + userSessionId?: string; + enablePolling?: boolean; + pollingInterval?: number; +} + +export function useSummaryUpdateManager( + resourceId: string, + options: UseUpdateManagerOptions = {} +) { + const { + isProject = false, + sessionIds, + projectId + } = options; + + const [lastEditTimestamp, setLastEditTimestamp] = useState(); + + // Use optimized summary status that leverages cached data + const { + data: summaryStatus, + isLoading, + error, + refetch + } = useSummaryStatusOptimized(resourceId, isProject); + + const updateSummary = useUpdateSummary(); + + // Calculate current status + const status = (() => { + if (isLoading || !summaryStatus) return RefreshStatus.Unknown; + + const delay = 30000; // 30 seconds + const now = Date.now(); + + if (summaryStatus.lastEdit > summaryStatus.lastSummaryUpdate) { + if (now - summaryStatus.lastEdit > delay && !updateSummary.isPending) { + return RefreshStatus.UpdatePending; + } + return RefreshStatus.Outdated; + } + + return RefreshStatus.UpToDate; + })(); + + // Auto-trigger updates when status becomes UpdatePending + useEffect(() => { + if (status === RefreshStatus.UpdatePending && + summaryStatus && + summaryStatus.lastEdit !== lastEditTimestamp) { + + console.log(`[useSummaryUpdateManager] Auto-triggering update for ${resourceId}`); + + updateSummary.mutate({ + sessionId: resourceId, + isProject, + sessionIds, + projectId + }); + + setLastEditTimestamp(summaryStatus.lastEdit); + } + }, [status, summaryStatus, lastEditTimestamp, resourceId, isProject, sessionIds, projectId, updateSummary]); + + // Manual update function + const updateNow = async () => { + if (updateSummary.isPending) { + console.log(`[useSummaryUpdateManager] Update already running for ${resourceId}`); + return; + } + + console.log(`[useSummaryUpdateManager] Manual update triggered for ${resourceId}`); + + try { + const result = await updateSummary.mutateAsync({ + sessionId: resourceId, + isProject, + sessionIds, + projectId + }); + + console.log(`[useSummaryUpdateManager] Update completed for ${resourceId}`); + return result; + } catch (error) { + console.error(`[useSummaryUpdateManager] Update failed for ${resourceId}:`, error); + throw error; + } + }; + + // Polling control + const startPolling = () => { + console.log(`[useSummaryUpdateManager] Starting polling for ${resourceId}`); + // TanStack Query handles polling automatically via refetchInterval + // We could force a refetch here if needed + refetch(); + }; + + const stopPolling = () => { + console.log(`[useSummaryUpdateManager] Stopping polling for ${resourceId}`); + // TanStack Query polling is controlled by the query options + // Individual queries can be paused, but it's handled at query level + }; + + return { + // State + status, + isRunning: updateSummary.isPending, + isLoading, + error: error || updateSummary.error, + lastEditTimestamp: summaryStatus?.lastEdit, + version: summaryStatus ? { + lastEdit: summaryStatus.lastEdit, + lastSummaryUpdate: summaryStatus.lastSummaryUpdate + } : undefined, + + // Actions + updateNow, + startPolling, + stopPolling, + refetch, + + // Compatibility with old SummaryUpdateManager interface + getState: () => ({ + isRunning: updateSummary.isPending, + lastEditTimestamp: summaryStatus?.lastEdit, + status, + version: summaryStatus ? { + lastEdit: summaryStatus.lastEdit, + lastSummaryUpdate: summaryStatus.lastSummaryUpdate + } : undefined, + listeners: new Set(), // Not used in React hook version + }), + + // React-specific + subscribe: () => { + // In React hooks, subscriptions are handled by the hook itself + // Return a no-op cleanup function for compatibility + return () => {}; + }, + }; +} \ No newline at end of file diff --git a/src/lib/summaryActions.ts b/src/lib/summaryActions.ts index dd63a8df..70a4545d 100644 --- a/src/lib/summaryActions.ts +++ b/src/lib/summaryActions.ts @@ -9,7 +9,65 @@ export async function checkSummaryNeedsUpdating(resourceId: string, isProject = } if (isProject) { - throw new Error('Automatic Project Summary updates are not implemented yet'); + try { + // Get workspace data + const workspace = await db.getWorkspaceById(resourceId); + if (!workspace) { + throw new Error('Workspace not found'); + } + + // Get all sessions in this workspace + const sessionIds = await db.getWorkspaceSessionIds(resourceId); + if (sessionIds.length === 0) { + // No sessions, use workspace last_modified + return { + lastEdit: new Date(workspace.last_modified).getTime(), + lastSummaryUpdate: workspace.summary ? new Date(workspace.last_modified).getTime() : 0, + resourceId + }; + } + + // Get all session data to find the latest edit + const [hostSessions, allUserData] = await Promise.all([ + Promise.all(sessionIds.map(id => db.getHostSessionById(id))), + Promise.all(sessionIds.map(id => db.getUsersBySessionId(id))) + ]); + + // Find the latest edit across all sessions + let latestEdit = new Date(workspace.last_modified).getTime(); + + // Check host session edits + for (const host of hostSessions) { + const hostEditTime = new Date(host.last_edit).getTime(); + if (hostEditTime > latestEdit) { + latestEdit = hostEditTime; + } + } + + // Check user session edits + for (const userData of allUserData) { + for (const user of userData) { + const userEditTime = new Date(user.last_edit).getTime(); + if (userEditTime > latestEdit) { + latestEdit = userEditTime; + } + } + } + + // Get workspace summary update time + const lastSummaryUpdate = workspace.summary + ? new Date(workspace.last_modified).getTime() + : 0; + + return { + lastEdit: latestEdit, + lastSummaryUpdate, + resourceId + }; + } catch (error) { + console.error('Error fetching workspace summary version:', error); + throw error; + } } else { try { // Get host session data @@ -69,6 +127,21 @@ export async function updateHostLastEdit(sessionId: string) { } } +export async function updateWorkspaceLastModified(workspaceId: string) { + if (!workspaceId) { + throw new Error('workspaceId is required'); + } + + try { + const now = new Date(); + await db.updateWorkspace(workspaceId, { last_modified: now }); + return { success: true, lastModified: now.toISOString() }; + } catch (error) { + console.error('Error updating workspace last_modified:', error); + throw error; + } +} + export async function fetchSummary(resourceId: string, isProject = false) { if (!resourceId) { throw new Error('resourceId is required'); diff --git a/src/lib/workspaceData.ts b/src/lib/workspaceData.ts index af55f684..a4b2bae9 100644 --- a/src/lib/workspaceData.ts +++ b/src/lib/workspaceData.ts @@ -1,12 +1,35 @@ import * as db from '@/lib/db'; import { ExtendedWorkspaceData } from '@/lib/types'; import { getSession } from '@auth0/nextjs-auth0'; -import { NewWorkspace } from './schema'; +import { NewWorkspace, HostSession, UserSession } from './schema'; import { hasAccessToResource } from './serverUtils'; +import { QueryClient } from '@tanstack/react-query'; -export async function fetchWorkspaceData(workspaceId: string): Promise { + +async function getAllAvailableSessionIds() { + // Fetch available sessions for linking if the user is logged in + const session = await getSession(); + const userId = session?.user?.sub; + const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); + const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); + let availableSessions: ExtendedWorkspaceData["availableSessions"] = []; + if (availableSessionsIds.length > 0) { + availableSessions = await db.getHostSessionsForIds(availableSessionsIds, [ + 'id', + 'topic', + 'start_time' + ]); + } + return availableSessions; +} + +export async function fetchWorkspaceData( + workspaceId: string, + queryClient?: QueryClient +): Promise { try { - console.log("Fetching initial project data for workspace ", workspaceId); + console.log("Fetching optimized project data for workspace ", workspaceId); + // First, check whether this workspace exists at all. If not, add a 'draft' mode with the current user as the owner: const workspaceExists = await db.hasWorkspace(workspaceId); if (!workspaceExists) { @@ -54,10 +77,40 @@ export async function fetchWorkspaceData(workspaceId: string): Promise db.getHostSessionById(id))), - Promise.all(sessionIds.map((id) => db.getUsersBySessionId(id))), - ]); + + let hostSessions: HostSession[]; + let allUserData: UserSession[][]; + + // Try to get from cache first if queryClient is provided + if (queryClient) { + console.log("Attempting to use cached session data..."); + const cachedHosts = sessionIds.map(id => + queryClient.getQueryData(['host', id]) + ); + const cachedUsers = sessionIds.map(id => + queryClient.getQueryData(['users', id]) + ); + + // Use cached data if all sessions are cached + if (cachedHosts.every(h => h) && cachedUsers.every(u => u)) { + console.log("Using cached session data for performance optimization"); + hostSessions = cachedHosts as HostSession[]; + allUserData = cachedUsers as UserSession[][]; + } else { + console.log("Cache miss - falling back to database queries"); + // Fallback to database if cache miss + [hostSessions, allUserData] = await Promise.all([ + Promise.all(sessionIds.map((id) => db.getHostSessionById(id))), + Promise.all(sessionIds.map((id) => db.getUsersBySessionId(id))), + ]); + } + } else { + // No queryClient provided, use database directly + [hostSessions, allUserData] = await Promise.all([ + Promise.all(sessionIds.map((id) => db.getHostSessionById(id))), + Promise.all(sessionIds.map((id) => db.getUsersBySessionId(id))), + ]); + } hostSessions.sort((a, b) => a.topic.localeCompare(b.topic)); @@ -83,7 +136,7 @@ export async function fetchWorkspaceData(workspaceId: string): Promise r.resource_id).filter((id) => id !== 'global'); - let availableSessions: ExtendedWorkspaceData["availableSessions"] = []; - if (availableSessionsIds.length > 0) { - availableSessions = await db.getHostSessionsForIds(availableSessionsIds, [ - 'id', - 'topic', - 'start_time' - ]); - } - return availableSessions; -} diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index a68bf88e..95ca0855 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,8 +1,12 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import * as db from '@/lib/db'; -import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace } from '@/lib/schema'; +import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace, HostSession, UserSession } from '@/lib/schema'; import { fetchWorkspaceData } from '@/lib/workspaceData'; import { linkSessionsToWorkspace, unlinkSessionFromWorkspace } from '@/lib/workspaceActions'; +import { checkSummaryNeedsUpdating, fetchSummary, updateUserLastEdit, updateHostLastEdit, updateWorkspaceLastModified } from '@/lib/summaryActions'; +import { checkSummaryAndMessageTimes } from '@/lib/clientUtils'; +import { createSummary, createMultiSessionSummary } from '@/lib/serverUtils'; +import { getSession } from '@auth0/nextjs-auth0'; // --- Query Keys --- @@ -10,13 +14,21 @@ const workspaceKey = (id: string) => ['workspace', id]; const hostKey = (id: string) => ['host', id]; const userKey = (sessionId: string) => ['users', sessionId]; const messageKey = (threadId: string) => ['messages', threadId]; +const summaryStatusKey = (sessionId: string) => ['summary-status', sessionId]; +const summaryContentKey = (sessionId: string) => ['summary-content', sessionId]; +// Workspace-specific query keys +const workspaceSessionsKey = (workspaceId: string) => ['workspace-sessions', workspaceId]; +const workspaceStatsKey = (sessionIds: string[]) => ['workspace-stats', sessionIds.sort()]; +const availableSessionsKey = () => ['available-sessions']; const staleTime = 1000 * 60; // 1 minute // --- Fetchers --- export function useWorkspace(workspaceId: string) { + const queryClient = useQueryClient(); + return useQuery({ queryKey: workspaceKey(workspaceId), - queryFn: () => fetchWorkspaceData(workspaceId), + queryFn: () => fetchWorkspaceData(workspaceId, queryClient), select: (data) => data ?? [], // Returns an empty array if data isn't available yet enabled: !!workspaceId, staleTime, @@ -51,6 +63,47 @@ export function useMessages(threadId: string) { }); } +// --- Workspace-specific hooks for optimized data fetching --- + +export function useWorkspaceSessionIds(workspaceId: string) { + return useQuery({ + queryKey: workspaceSessionsKey(workspaceId), + queryFn: () => db.getWorkspaceSessionIds(workspaceId), + enabled: !!workspaceId, + staleTime, + }); +} + +export function useWorkspaceStats(sessionIds: string[]) { + return useQuery({ + queryKey: workspaceStatsKey(sessionIds), + queryFn: () => db.getNumUsersAndMessages(sessionIds), + enabled: sessionIds.length > 0, + staleTime, + }); +} + +export function useAvailableSessions() { + return useQuery({ + queryKey: availableSessionsKey(), + queryFn: async () => { + // Move getAllAvailableSessionIds logic here + const session = await getSession(); + const userId = session?.user?.sub; + const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); + const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); + + if (availableSessionsIds.length > 0) { + return await db.getHostSessionsForIds(availableSessionsIds, [ + 'id', 'topic', 'start_time' + ]); + } + return []; + }, + staleTime, + }); +} + // --- Mutations --- export function useUpsertWorkspace() { const queryClient = useQueryClient(); @@ -104,7 +157,12 @@ export function useRemoveSession() { onSuccess: (_data, sessionId) => { queryClient.invalidateQueries({ queryKey: hostKey(sessionId) }); queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); - // Optionally, invalidate workspace/session lists if needed + // Invalidate available sessions since this session was deleted + queryClient.invalidateQueries({ queryKey: availableSessionsKey() }); + // Invalidate all workspace queries since they might reference this session + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + queryClient.invalidateQueries({ queryKey: ['workspace-sessions'] }); + queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); }, }); } @@ -116,6 +174,9 @@ export function useLinkSessionsToWorkspace() { await linkSessionsToWorkspace(workspaceId, sessionIds), onSuccess: (_result, variables) => { queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); + queryClient.invalidateQueries({ queryKey: workspaceSessionsKey(variables.workspaceId) }); + // Invalidate workspace stats for the new session configuration + queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); }, }); } @@ -127,6 +188,131 @@ export function useUnlinkSessionFromWorkspace() { await unlinkSessionFromWorkspace(workspaceId, sessionId), onSuccess: (_result, variables) => { queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); + queryClient.invalidateQueries({ queryKey: workspaceSessionsKey(variables.workspaceId) }); + // Invalidate workspace stats for the new session configuration + queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); + }, + }); +} + +// --- Summary Operations --- + + +export function useSummaryStatusOptimized(sessionId: string, isProject = false) { + const queryClient = useQueryClient(); + + return useQuery({ + queryKey: summaryStatusKey(sessionId), + queryFn: async () => { + // For projects, we need different caching strategy + if (isProject) { + // For workspaces, check if we have workspace data cached + const cachedWorkspace = queryClient.getQueryData(workspaceKey(sessionId)); + if (cachedWorkspace) { + // We have workspace data, but still need to call server action + // for complex multi-session timestamp calculation + return checkSummaryNeedsUpdating(sessionId, true); + } + // No workspace cache, fallback to server action + return checkSummaryNeedsUpdating(sessionId, true); + } + + // For sessions, try to use cached data first to avoid database calls + const cachedHost = queryClient.getQueryData(hostKey(sessionId)); + const cachedUsers = queryClient.getQueryData(userKey(sessionId)); + + if (cachedHost && cachedUsers) { + const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUsers); + const lastUserEdit = cachedUsers.reduce((latest: number, user: UserSession) => { + const lastEditTime = new Date(user.last_edit).getTime(); + return lastEditTime > latest ? lastEditTime : latest; + }, 0); + + return { + lastEdit: Math.max(lastMessage, lastUserEdit), + lastSummaryUpdate, + resourceId: sessionId + }; + } + + // Fallback to server action if no cached data + return checkSummaryNeedsUpdating(sessionId, false); + }, + enabled: !!sessionId, + staleTime: 10000, + refetchInterval: 10000, + }); +} + +export function useSummaryContent(sessionId: string) { + return useQuery({ + queryKey: summaryContentKey(sessionId), + queryFn: () => fetchSummary(sessionId), + enabled: !!sessionId, + staleTime, + }); +} + +export function useUpdateSummary() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ sessionId, isProject = false, sessionIds, projectId }: { + sessionId: string; + isProject?: boolean; + sessionIds?: string[]; + projectId?: string; + }) => { + if (isProject) { + return createMultiSessionSummary( + sessionIds?.length ? sessionIds : [sessionId], + projectId ?? sessionId + ); + } else { + return createSummary(sessionId); + } + }, + onSuccess: (summary, variables) => { + // Update cache with new summary content + queryClient.setQueryData(summaryContentKey(variables.sessionId), summary); + // Invalidate status to reflect that summary is now up to date + queryClient.invalidateQueries({ queryKey: summaryStatusKey(variables.sessionId) }); + // Invalidate host session to update last_summary_update timestamp + queryClient.invalidateQueries({ queryKey: hostKey(variables.sessionId) }); + }, + }); +} + +export function useUpdateUserLastEdit() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (userSessionId: string) => updateUserLastEdit(userSessionId), + onSuccess: (_result, _userSessionId) => { + // We need to find which session this user belongs to + // For now, invalidate all user queries - could be optimized + queryClient.invalidateQueries({ queryKey: ['users'] }); + queryClient.invalidateQueries({ queryKey: ['summary-status'] }); + }, + }); +} + +export function useUpdateHostLastEdit() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (sessionId: string) => updateHostLastEdit(sessionId), + onSuccess: (_result, sessionId) => { + queryClient.invalidateQueries({ queryKey: hostKey(sessionId) }); + queryClient.invalidateQueries({ queryKey: summaryStatusKey(sessionId) }); + }, + }); +} + +export function useUpdateWorkspaceLastModified() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (workspaceId: string) => updateWorkspaceLastModified(workspaceId), + onSuccess: (_result, workspaceId) => { + queryClient.invalidateQueries({ queryKey: workspaceKey(workspaceId) }); + queryClient.invalidateQueries({ queryKey: summaryStatusKey(workspaceId) }); }, }); } \ No newline at end of file diff --git a/src/summary/SummaryUpdateManager.ts b/src/summary/SummaryUpdateManager.ts deleted file mode 100644 index 6b38d806..00000000 --- a/src/summary/SummaryUpdateManager.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { createSummary, createMultiSessionSummary } from '@/lib/serverUtils'; -import { checkSummaryNeedsUpdating } from '@/lib/summaryActions'; -import { updateUserLastEdit, updateHostLastEdit } from '@/lib/summaryActions'; -import { mutate } from 'swr'; - -export enum RefreshStatus { - Unknown, - Outdated, - UpdatePending, - UpToDate, -} - -interface UpdateState { - isRunning: boolean; - lastEditTimestamp?: number; - status: RefreshStatus; - version?: { lastEdit: number; lastSummaryUpdate: number }; - pollingIntervalId?: NodeJS.Timeout; - listeners: Set<(state: UpdateState) => void>; -} - -export interface ManagerOpts { - isProject?: boolean; - sessionIds?: string[]; - projectId?: string; - // Check: Needed? - source?: 'participants' | 'chat' | 'ui' | 'auto'; - userSessionId?: string; -} - -class SummaryUpdateManagerClass { - private updates = new Map(); - - startPolling(resourceId: string, interval = 10000) { - this.stopPolling(resourceId); // Ensure no duplicate intervals - console.log("Start polling for", resourceId); - const poll = async () => { - console.log(`[SummaryUpdateManager] Polling for ${resourceId}`); - this.handleUpdateTimes(resourceId); - }; - - poll(); // Initial poll - - const intervalId = setInterval(poll, interval); - this.getOrCreateState(resourceId).pollingIntervalId = intervalId; - } - - stopPolling(resourceId: string) { - const state = this.updates.get(resourceId); - if (state?.pollingIntervalId) { - clearInterval(state.pollingIntervalId); - state.pollingIntervalId = undefined; - console.log("Stopped polling for", resourceId); - } - } - - // --- Status and version logic --- - private getOrCreateState(resourceId: string): UpdateState { - if (!this.updates.has(resourceId)) { - this.updates.set(resourceId, { - isRunning: false, - status: RefreshStatus.Unknown, - listeners: new Set(), - }); - } - return this.updates.get(resourceId)!; - } - - private async handleUpdateTimes(resourceId: string) { - const state = this.getOrCreateState(resourceId); - try { - const updateTimes = await checkSummaryNeedsUpdating(resourceId); - - const delay = 30000; - if (updateTimes.lastEdit > updateTimes.lastSummaryUpdate) { - console.log(`[SummaryUpdateManager] Update needed for ${resourceId}`); - state.status = RefreshStatus.UpdatePending; - if (Date.now() - updateTimes.lastEdit > delay && !state.isRunning) { - console.log(`[SummaryUpdateManager] Triggering update for ${resourceId} (30s delay has passed...)`); - this.updateNow(resourceId, { source: 'auto' }); - state.lastEditTimestamp = updateTimes.lastEdit; - } - } else { - state.status = RefreshStatus.UpToDate; - } - } catch (error) { - console.error('Failed to check update times:', error); - } - this.notify(resourceId); - } - - // --- Listener logic for React hook --- - subscribe(resourceId: string, listener: (state: UpdateState) => void) { - this.getOrCreateState(resourceId).listeners.add(listener); - return () => this.getOrCreateState(resourceId).listeners.delete(listener); - } - - private notify(resourceId: string, message?: string) { - const state = this.updates.get(resourceId); - if (state) { - if (message) { - console.log(`[SummaryUpdateManager] Notifying listeners for ${resourceId}: ${message}`); - } - state.listeners.forEach((fn) => fn(state)); - } - } - - /** - * Execute summary update now - */ - async updateNow(resourceId: string, opts: ManagerOpts = {}): Promise { - const state = this.getOrCreateState(resourceId); - if (state.isRunning) { - console.log(`[SummaryUpdateManager] Update already running for ${resourceId}`); - return; - } - state.isRunning = true; - this.notify(resourceId, "Update started"); - - try { - console.log(`[SummaryUpdateManager] Executing update for ${resourceId}`, opts); - - let summary: string; - if (opts.isProject) { - summary = await createMultiSessionSummary( - opts.sessionIds?.length ? opts.sessionIds : [resourceId], - opts.projectId ?? resourceId - ); - } else { - summary = await createSummary(resourceId); - } - - // Update SWR cache with new summary content and invalidate version cache - mutate(['summary-content', resourceId], summary, false); - mutate(key => - typeof key === 'string' && - (key.includes('summary-version') || key.includes(resourceId)) - ); - - console.log(`[SummaryUpdateManager] Update completed for ${resourceId}`); - return summary; - - } catch (error) { - console.error(`[SummaryUpdateManager] Update failed for ${resourceId}:`, error); - } finally { - // Clean up state - state.isRunning = false; - state.status = RefreshStatus.UpToDate; - this.notify(resourceId, "Update finished"); - } - } - - getState(resourceId: string): UpdateState { - return this.getOrCreateState(resourceId); - } - - clearState(resourceId: string): void { - this.stopPolling(resourceId); - this.updates.delete(resourceId); - } -} - -// Export singleton instance -export const SummaryUpdateManager = new SummaryUpdateManagerClass(); From 9245a5f5b5b72ae25090d81798a6cc842ae84d11 Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 28 Jul 2025 15:59:57 +1200 Subject: [PATCH 08/27] Convert more usages --- src/app/(dashboard)/sessions-table.tsx | 17 ++- .../SessionResult/SessionResultControls.tsx | 28 +++- src/hooks/useChat.ts | 120 ++++++------------ src/hooks/useSummary.ts | 20 ++- src/lib/db.ts | 6 +- src/stores/SessionStore.ts | 12 +- 6 files changed, 91 insertions(+), 112 deletions(-) diff --git a/src/app/(dashboard)/sessions-table.tsx b/src/app/(dashboard)/sessions-table.tsx index d91b3798..8a034bec 100644 --- a/src/app/(dashboard)/sessions-table.tsx +++ b/src/app/(dashboard)/sessions-table.tsx @@ -5,7 +5,7 @@ import { Session } from './session'; import { Key, useEffect, useState } from 'react'; import SortableTable from '@/components/SortableTable'; import { HostSession } from '@/lib/schema'; -import * as db from '@/lib/db'; +import { useWorkspaceStats } from '@/stores/SessionStore'; import { getUserStats, SessionStatus } from '@/lib/clientUtils'; export type SessionTableData = { @@ -54,14 +54,17 @@ export function SessionsTable({ sessions }: { sessions: HostSession[] }) { const [tableSessions, setTableSessions] = useState([]); + + const sessionIds = sessions.map((s) => s.id); + const { data: sessionToUserStats } = useWorkspaceStats(sessionIds); + useEffect(() => { - setAsSessionTableData(sessions); - }, []); + if (sessionToUserStats) { + setAsSessionTableData(sessions, sessionToUserStats); + } + }, [sessions, sessionToUserStats]); - async function setAsSessionTableData(sessions: HostSession[]) { - const sessionToUserStats = await db.getNumUsersAndMessages( - sessions.map((s) => s.id), - ); + function setAsSessionTableData(sessions: HostSession[], sessionToUserStats: any) { const asSessionTableData = sessions .map((session) => { const { totalUsers, finishedUsers } = getUserStats( diff --git a/src/components/SessionResult/SessionResultControls.tsx b/src/components/SessionResult/SessionResultControls.tsx index 917d1679..61d27473 100644 --- a/src/components/SessionResult/SessionResultControls.tsx +++ b/src/components/SessionResult/SessionResultControls.tsx @@ -4,8 +4,7 @@ import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { LoaderCircle, Settings, Copy, InfoIcon, Trash2 } from 'lucide-react'; -import * as db from '@/lib/db'; -import { useUpdateSummary } from '@/stores/SessionStore'; +import { useUpdateSummary, useUpsertHostSession, useRemoveSession } from '@/stores/SessionStore'; import { cloneSession } from '@/lib/serverUtils'; import { useRouter } from 'next/navigation'; import { toast } from 'hooks/use-toast'; @@ -26,6 +25,7 @@ import { DialogDescription, DialogFooter, } from '@/components/ui/dialog'; +import { NewHostSession } from '@/lib/schema'; interface SessionResultControlsProps { id: string; @@ -54,6 +54,8 @@ export default function SessionResultControls({ useState(crossPollination); const router = useRouter(); const updateSummaryMutation = useUpdateSummary(); + const upsertHostSessionMutation = useUpsertHostSession(); + const removeSessionMutation = useRemoveSession(); const handlePromptChange = async ( newPrompt: string, @@ -65,7 +67,10 @@ export default function SessionResultControls({ ? { prompt: newPrompt } : { summary_prompt: newPrompt }; - await db.updateHostSession(id, updateData); + await upsertHostSessionMutation.mutateAsync({ + id, + ...updateData + } as NewHostSession); } catch (error) { console.error('Failed to update prompt:', error); toast({ @@ -82,7 +87,10 @@ export default function SessionResultControls({ setLocalCrossPollination(checked); // Update database - await db.updateHostSession(id, { cross_pollination: checked }); + await upsertHostSessionMutation.mutateAsync({ + id, + cross_pollination: checked + } as NewHostSession); // Show success toast toast({ @@ -107,12 +115,18 @@ export default function SessionResultControls({ }; const finishSession = async () => { - await db.deactivateHostSession(id); + await upsertHostSessionMutation.mutateAsync({ + id, + active: false + } as NewHostSession); }; const reopenSession = async () => { console.log('Reopening session'); - await db.updateHostSession(id, { active: true }); + await upsertHostSessionMutation.mutateAsync({ + id, + active: true + } as NewHostSession); }; const updateSummary = async () => { @@ -156,7 +170,7 @@ export default function SessionResultControls({ const handleDeleteSession = async () => { setIsDeleting(true); try { - await db.deleteHostSession(id); + await removeSessionMutation.mutateAsync(id); toast({ title: 'Session deleted', description: 'The session has been successfully deleted.', diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 941a8a5a..67adf421 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -5,9 +5,10 @@ import * as db from '@/lib/db'; import * as llama from '../app/api/llamaUtils'; import { OpenAIMessage, OpenAIMessageWithContext } from '@/lib/types'; import { UserProfile, useUser } from '@auth0/nextjs-auth0/client'; -import { Message } from '@/lib/schema'; +import { Message, NewUserSession } from '@/lib/schema'; import { getUserNameFromContext } from '@/lib/clientUtils'; import { getUserSessionById, getAllChatMessagesInOrder } from '@/lib/db'; +import { useInsertMessages, useMessages, useUpsertUserSessions, useUserSession } from '@/stores/SessionStore'; export interface UseChatOptions { sessionIds?: string[]; @@ -60,6 +61,11 @@ export function useChat(options: UseChatOptions) { } | null>(null); const [errorToastMessage, setErrorToastMessage] = useState(''); const { user } = useUser(); + + // Get existing user session data if userSessionId is provided + const { data: existingUserSession, isLoading: isLoadingUserSession } = useUserSession(userSessionId || ''); + const threadId = existingUserSession?.thread_id || ''; + const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); const placeholder = placeholderText ? placeholderText @@ -81,6 +87,7 @@ export function useChat(options: UseChatOptions) { }; const [isLoading, setIsLoading] = useState(false); + const messageInserter = useInsertMessages(); const textareaRef = useRef(null); const messagesEndRef = useRef(null); const [isParticipantSuggestionLoading, setIsParticipantSuggestionLoading] = @@ -214,16 +221,11 @@ export function useChat(options: UseChatOptions) { }) .join('; ') : ''; - db.insertChatMessage({ + messageInserter.mutate({ thread_id: threadIdRef.current, role: 'user', content: `User shared the following context:\n${contextString}`, created_at: new Date(), - }).catch((error) => { - console.log('Error in insertChatMessage: ', error); - showErrorToast( - 'Oops, something went wrong storing your message. This is uncomfortable; but please just continue if you can', - ); }); console.log('Inserting new session with initial data: ', data); return db @@ -258,23 +260,28 @@ export function useChat(options: UseChatOptions) { createThreadInProgressRef.current = true; // Check if we have an existing userSessionId to restore - if (userSessionId) { - restoreExistingThread(userSessionId).then((restored: boolean) => { - if (!restored) { - // Fallback to creating new thread if restoration fails - createThread( - context, - sessionIds && sessionIds.length ? sessionIds[0] : undefined, - user ? user : 'id', - userName, - userContext, - ).then((threadSessionId) => { - handleSubmit(undefined, true, threadSessionId); - }); - } - }); - } else { - // No existing session, create new thread + if (userSessionId && existingUserSession && !isLoadingUserSession) { + // Restore existing thread + threadIdRef.current = existingUserSession.thread_id; + if (onThreadIdReceived) { + onThreadIdReceived(existingUserSession.thread_id); + } + + // Filter out the initial context message if present + if (existingMessages) { + const filteredMessages = existingMessages.filter( + msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) + ).map(msg => ({ + role: msg.role as 'user' | 'assistant', + content: msg.content, + is_final: msg.is_final, + })); + + setMessages(filteredMessages); + console.log(`[i] Successfully restored ${existingMessages.length} messages for thread ${existingUserSession.thread_id}`); + } + } else if (!userSessionId || (!existingUserSession && !isLoadingUserSession) || (!existingMessages && isLoadingMessages)) { + // No existing session or session not found, create new thread createThread( context, sessionIds && sessionIds.length ? sessionIds[0] : undefined, @@ -286,52 +293,8 @@ export function useChat(options: UseChatOptions) { }); } } - }, [userContext]); + }, [userContext, userSessionId, existingUserSession, isLoadingUserSession]); - /** - * Restores an existing thread from a userSessionId - * @param userSessionId The existing user session ID - * @returns {Promise} True if restoration was successful - */ - async function restoreExistingThread(userSessionId: string): Promise { - try { - console.log(`[i] Attempting to restore thread for userSessionId: ${userSessionId}`); - - // Get the existing user session - const existingSession = await getUserSessionById(userSessionId); - if (!existingSession) { - console.log(`[i] No existing session found for userSessionId: ${userSessionId}`); - return false; - } - - // Set the thread ID to the existing one - threadIdRef.current = existingSession.thread_id; - if (onThreadIdReceived) { - onThreadIdReceived(existingSession.thread_id); - } - - // Load existing messages for this thread - const existingMessages = await getAllChatMessagesInOrder(existingSession.thread_id); - - // Filter out the initial context message if present - const filteredMessages = existingMessages.filter( - msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) - ).map(msg => ({ - role: msg.role as 'user' | 'assistant', - content: msg.content, - is_final: msg.is_final, - })); - - setMessages(filteredMessages); - - console.log(`[i] Successfully restored ${existingMessages.length} messages for thread ${existingSession.thread_id}`); - return true; - - } catch (error) { - console.error('[!] Error restoring existing thread:', error); - return false; - } - } const handleSubmit = async ( e?: React.FormEvent, @@ -377,16 +340,11 @@ export function useChat(options: UseChatOptions) { console.log( `[i] Inserting chat message for user session ${userSessionId}`, ); - db.insertChatMessage({ + messageInserter.mutate({ thread_id: threadIdRef.current, role: 'user', content: messageText, created_at: now, - }).catch((error) => { - console.log('Error in insertChatMessage: ', error); - showErrorToast( - 'Oops, something went wrong storing your message. This is uncomfortable; but please just continue if you can', - ); }); } @@ -436,18 +394,16 @@ export function useChat(options: UseChatOptions) { if (userSessionId || userSessionIdFromThread) { Promise.all([ - db.insertChatMessage({ + messageInserter.mutateAsync({ ...answer, thread_id: threadIdRef.current, created_at: now, }), userSessionId || userSessionIdFromThread - ? db.updateUserSession( - userSessionId || userSessionIdFromThread!, - { - last_edit: now, - }, - ) + ? useUpsertUserSessions().mutateAsync({ + id: userSessionId || userSessionIdFromThread!, + last_edit: now, + } as NewUserSession) // It is not a _new_ user session, but this type allows us to omit all the other fields! : Promise.resolve(), ]).catch((error) => { console.log( diff --git a/src/hooks/useSummary.ts b/src/hooks/useSummary.ts index fe1261f0..d0db1177 100644 --- a/src/hooks/useSummary.ts +++ b/src/hooks/useSummary.ts @@ -1,15 +1,13 @@ -import useSWR from 'swr'; +import { useQuery } from '@tanstack/react-query'; import { fetchSummary } from '@/lib/summaryActions'; export function useSummary(resourceId: string, initialSummary?: string, isProject = false) { - return useSWR( - resourceId ? ['summary-content', resourceId] : null, - ([_, id]) => fetchSummary(id, isProject), - { - fallbackData: initialSummary, - revalidateOnFocus: false, - refreshWhenHidden: false, - refreshWhenOffline: false - } - ); + return useQuery({ + queryKey: ['summary-content', resourceId], + queryFn: () => fetchSummary(resourceId, isProject), + enabled: !!resourceId, + initialData: initialSummary, + staleTime: 1000 * 60, // 1 minute + refetchOnWindowFocus: false, + }); } diff --git a/src/lib/db.ts b/src/lib/db.ts index c3f63c15..9f4ad3e4 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -706,7 +706,7 @@ export async function hasWorkspace(id: string): Promise { export async function getWorkspaceById( id: string, ): Promise { - console.log('[i] Database Operation: getWorkspaceById'); + console.log(`[i] Database Operation: getWorkspaceById(${id})`); try { const db = await dbPromise; const result = await db @@ -880,7 +880,7 @@ export async function removeSessionFromWorkspace( export async function getWorkspaceSessionIds( workspaceId: string, ): Promise { - console.log('[i] Database Operation: getWorkspaceSessionIds'); + console.log(`[i] Database Operation: getWorkspaceSessionIds(${workspaceId})`); try { const db = await dbPromise; const results = await db @@ -899,7 +899,7 @@ export async function getWorkspaceSessionIds( export async function getWorkspacesForSession( sessionId: string, ): Promise[]> { - console.log('[i] Database Operation: getWorkspacesForSession'); + console.log(`[i] Database Operation: getWorkspacesForSession(${sessionId})`); try { const db = await dbPromise; const workspaces = await db diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 95ca0855..d4e70fca 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -25,7 +25,6 @@ const staleTime = 1000 * 60; // 1 minute // --- Fetchers --- export function useWorkspace(workspaceId: string) { const queryClient = useQueryClient(); - return useQuery({ queryKey: workspaceKey(workspaceId), queryFn: () => fetchWorkspaceData(workspaceId, queryClient), @@ -55,6 +54,15 @@ export function useUserSessions(sessionId: string) { }); } +export function useUserSession(userSessionId: string) { + return useQuery({ + queryKey: ['user-session', userSessionId], + queryFn: () => db.getUserSessionById(userSessionId), + enabled: !!userSessionId, + staleTime, + }); +} + export function useMessages(threadId: string) { return useQuery({ queryKey: messageKey(threadId), @@ -140,7 +148,7 @@ export function useUpsertUserSessions() { }); } -export function useUpsertMessages() { +export function useInsertMessages() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (message: NewMessage) => db.insertChatMessage(message), From 6d05d7163130c0aa865e3684fe06745f9f93db4e Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 28 Jul 2025 17:48:16 +1200 Subject: [PATCH 09/27] More TanStack --- src/app/(dashboard)/ProjectsGrid.tsx | 6 +- src/app/(dashboard)/page.tsx | 3 - src/app/(dashboard)/workspaces-table.tsx | 304 ------------------ src/app/api/llamaUtils.ts | 4 +- src/app/chat/StandaloneChat.tsx | 19 +- src/app/workspace/[w_id]/WorkspaceContent.tsx | 32 +- src/app/workspace/[w_id]/actions.ts | 11 - src/components/AddToProjectDialog.tsx | 16 +- .../SessionResult/ParticipantSessionRow.tsx | 146 +-------- .../SessionResult/ResultTabs/index.tsx | 38 +-- .../SessionResult/TranscriptPopup.tsx | 137 ++++++++ src/components/chat/ChatInterface.tsx | 19 +- src/components/workspace/WorkspaceHero.tsx | 14 +- src/hooks/useChat.ts | 20 +- src/hooks/useSummaryUpdateManager.ts | 4 +- src/lib/db.ts | 17 +- src/stores/SessionStore.ts | 25 +- 17 files changed, 231 insertions(+), 584 deletions(-) delete mode 100644 src/app/(dashboard)/workspaces-table.tsx create mode 100644 src/components/SessionResult/TranscriptPopup.tsx diff --git a/src/app/(dashboard)/ProjectsGrid.tsx b/src/app/(dashboard)/ProjectsGrid.tsx index c15b168c..55932fa2 100644 --- a/src/app/(dashboard)/ProjectsGrid.tsx +++ b/src/app/(dashboard)/ProjectsGrid.tsx @@ -14,7 +14,6 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { deleteWorkspace } from './actions'; -import { updateWorkspaceDetails } from '../workspace/[w_id]/actions'; import { useRouter } from 'next/navigation'; import { Input } from '@/components/ui/input'; import { @@ -23,6 +22,7 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; +import { useUpsertWorkspace } from '@/stores/SessionStore'; function CreateWorkspaceButton({ text = 'New Project' }: { text?: string }) { return ( @@ -46,6 +46,7 @@ function ProjectCard({ workspace }: { workspace: WorkspaceWithSessions }) { const [isRenaming, setIsRenaming] = React.useState(false); const [newTitle, setNewTitle] = React.useState(workspace.title); const [isRenameDialogOpen, setIsRenameDialogOpen] = React.useState(false); + const upsertWorkspace = useUpsertWorkspace(); // Update newTitle when workspace title changes React.useEffect(() => { @@ -76,7 +77,8 @@ function ProjectCard({ workspace }: { workspace: WorkspaceWithSessions }) { setIsRenaming(true); try { - await updateWorkspaceDetails(workspace.id, { title: newTitle.trim() }); + await upsertWorkspace.mutateAsync({ id: workspace.id, data: { title: newTitle.trim() } }); + setIsRenameDialogOpen(false); // Refresh the page to update the list router.refresh(); diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/page.tsx index de5d136d..75935340 100644 --- a/src/app/(dashboard)/page.tsx +++ b/src/app/(dashboard)/page.tsx @@ -1,8 +1,6 @@ -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { ChevronRight, PlusCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { SessionsTable } from './sessions-table'; -import { WorkspacesTable } from './workspaces-table'; import * as db from '@/lib/db'; import Link from 'next/link'; import { cache } from 'react'; @@ -13,7 +11,6 @@ import { HostSession, Workspace } from '@/lib/schema'; import DonateBanner from '@/components/DonateBanner'; import { getSession } from '@auth0/nextjs-auth0'; import ProjectsGrid from './ProjectsGrid'; -import { Textarea } from '@/components/ui/textarea'; import CreateSessionInputClient from './CreateSessionInputClient'; export const dynamic = 'force-dynamic'; // getHostSessions is using auth, which can only be done client side diff --git a/src/app/(dashboard)/workspaces-table.tsx b/src/app/(dashboard)/workspaces-table.tsx deleted file mode 100644 index 68ab86e2..00000000 --- a/src/app/(dashboard)/workspaces-table.tsx +++ /dev/null @@ -1,304 +0,0 @@ -'use client'; - -import { Card, CardContent } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { - ExternalLink, - ChevronRight, - ChevronDown, - MoreHorizontal, - Trash2, - Share2, -} from 'lucide-react'; -import Link from 'next/link'; -import { Badge } from '@/components/ui/badge'; -import { TableCell, TableRow } from '@/components/ui/table'; -import SortableTable from '@/components/SortableTable'; -import { useState } from 'react'; -import { HostSession } from '@/lib/schema'; -import { WorkspaceWithSessions } from './page'; -import { encryptId } from '@/lib/encryptionUtils'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { deleteWorkspace } from './actions'; -import { removeSessionFromWorkspace } from '@/lib/sessionWorkspaceActions'; -import ShareSettings from '@/components/ShareSettings'; -import { toast } from 'hooks/use-toast'; - -function SessionRow({ - session, - workspaceId, - onRemove, -}: { - session: HostSession; - workspaceId: string; - onRemove: (sessionId: string) => void; -}) { - const [isRemoving, setIsRemoving] = useState(false); - const [showShareDialog, setShowShareDialog] = useState(false); - - const handleRemoveSession = async () => { - if (isRemoving) return; - - setIsRemoving(true); - try { - const result = await removeSessionFromWorkspace(workspaceId, session.id); - if (result.success) { - onRemove(session.id); - toast({ - title: 'Session removed', - description: 'The session has been removed from this project.', - }); - } else { - toast({ - title: 'Failed to remove session', - description: - result.error || 'An error occurred while removing the session.', - variant: 'destructive', - }); - } - } catch (error) { - console.error('Error removing session:', error); - toast({ - title: 'Failed to remove session', - description: 'An error occurred while removing the session.', - variant: 'destructive', - }); - } finally { - setIsRemoving(false); - } - }; - - return ( - - - -
{session.topic}
- -
- - - {session.active ? 'Active' : 'Finished'} - - - - - {new Intl.DateTimeFormat(undefined, { - dateStyle: 'medium', - timeStyle: 'short', - }).format(new Date(session.start_time))} - - -
- - - - - - - - - setShowShareDialog(true)}> - - Share - - - - {isRemoving ? 'Removing...' : 'Remove from Project'} - - - -
-
- {showShareDialog && ( - setShowShareDialog(false)} - /> - )} -
- ); -} - -function WorkspaceRow({ workspace }: { workspace: WorkspaceWithSessions }) { - const [isExpanded, setIsExpanded] = useState(false); - const [showShareDialog, setShowShareDialog] = useState(false); - const [localSessions, setLocalSessions] = useState(workspace.sessions); - - const handleDelete = async () => { - const workspaceId = workspace.id; - console.log('Deleting project: ', workspaceId); - await deleteWorkspace(workspaceId); - }; - - const handleRemoveSession = (sessionId: string) => { - setLocalSessions((prev) => - prev.filter((session) => session.id !== sessionId) - ); - }; - - return ( - <> - - -
- - -
-
{workspace.title}
-
- {workspace.description} -
-
- -
-
- - - {localSessions.length} Sessions - - - - {new Intl.DateTimeFormat(undefined, { - dateStyle: 'medium', - timeStyle: 'short', - }).format(new Date(workspace.created_at))} - - -
- - - - - - - - - setShowShareDialog(true)}> - - Share - - - - Delete - - - -
-
-
- {isExpanded && - localSessions && - localSessions.map((session) => ( - - ))} - {showShareDialog && ( - setShowShareDialog(false)} - /> - )} - - ); -} - -export function WorkspacesTable({ - workspaces, -}: { - workspaces: WorkspaceWithSessions[]; -}) { - const tableHeaders = [ - { - label: 'Project', - sortKey: 'title', - className: 'cursor-pointer', - }, - { - label: 'Sessions', - sortKey: 'num_sessions', - className: 'cursor-pointer', - }, - { - label: 'Created', - sortKey: 'created_at', - className: 'cursor-pointer', - sortBy: (sortDirection: string, a: string, b: string) => { - return sortDirection === 'asc' - ? new Date(a).getTime() - new Date(b).getTime() - : new Date(b).getTime() - new Date(a).getTime(); - }, - }, - ]; - - const getTableRow = (workspace: WorkspaceWithSessions, index: number) => { - return ; - }; - - return ( - - - - - - ); -} diff --git a/src/app/api/llamaUtils.ts b/src/app/api/llamaUtils.ts index bf34e374..cb6b18b8 100644 --- a/src/app/api/llamaUtils.ts +++ b/src/app/api/llamaUtils.ts @@ -6,9 +6,7 @@ import { ChatMessage } from 'llamaindex'; import { NextResponse } from 'next/server'; import { getAllChatMessagesInOrder, - getHostSessionById, - updateUserSession, - increaseSessionsCount, + getHostSessionById } from '@/lib/db'; import { initializeCrossPollination } from '@/lib/crossPollination'; import { getLLM } from '@/lib/modelConfig'; diff --git a/src/app/chat/StandaloneChat.tsx b/src/app/chat/StandaloneChat.tsx index 8198dfd7..843230bb 100644 --- a/src/app/chat/StandaloneChat.tsx +++ b/src/app/chat/StandaloneChat.tsx @@ -4,15 +4,12 @@ import { memo, useEffect, useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { useUser } from '@auth0/nextjs-auth0/client'; import { OpenAIMessage } from '@/lib/types'; -import { - increaseSessionsCount, - updateUserSession, -} from '@/lib/db'; import { LoadingOverlay } from '@/components/chat/LoadingOverlay'; import { SessionModal } from '@/components/chat/SessionModal'; import { ChatInterface } from '@/components/chat/ChatInterface'; import { QuestionInfo } from 'app/create/types'; -import { useHostSession } from '@/stores/SessionStore'; +import { useHostSession, useUpsertUserSessions } from '@/stores/SessionStore'; +import { NewUserSession } from '@/lib/schema'; const StandaloneChat = () => { const [message, setMessage] = useState({ @@ -25,7 +22,6 @@ Please type your name or "anonymous" if you prefer const { user } = useUser(); const searchParams = useSearchParams(); const sessionId = searchParams.get('s'); - const assistantId = searchParams.get('a'); const { data: hostData } = useHostSession(sessionId || ''); const [userSessionId, setUserSessionId] = useState(() => { @@ -52,6 +48,8 @@ Please type your name or "anonymous" if you prefer return {}; }); + const upsertUserSession = useUpsertUserSessions(); + useEffect(() => { setIsMounted(true); const loadData = async () => { @@ -64,13 +62,11 @@ Please type your name or "anonymous" if you prefer const finishSession = () => { setIsLoading(true); setShowModal(true); - updateUserSession(userSessionId!, { + upsertUserSession.mutateAsync({ + id: userSessionId!, active: false, last_edit: new Date(), - }) - .then(() => { - increaseSessionsCount(sessionId!, 'num_finished'); - }) + } as NewUserSession) .then(() => { setIsLoading(false); setUserFinished(true); @@ -182,7 +178,6 @@ Please type your name or "anonymous" if you prefer isMounted={isMounted} isLoading={isLoading} message={message} - assistantId={assistantId ?? undefined} userContext={userContext} questions={hostData?.questions} /> diff --git a/src/app/workspace/[w_id]/WorkspaceContent.tsx b/src/app/workspace/[w_id]/WorkspaceContent.tsx index cd164d24..c0a04b94 100644 --- a/src/app/workspace/[w_id]/WorkspaceContent.tsx +++ b/src/app/workspace/[w_id]/WorkspaceContent.tsx @@ -3,18 +3,15 @@ import ResultTabs from '@/components/SessionResult/ResultTabs'; import WorkspaceHero from '@/components/workspace/WorkspaceHero'; import ShareSettings from '@/components/ShareSettings'; -import { - ResultTabsVisibilityConfig, - Workspace, -} from '@/lib/schema'; +import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { usePermissions } from '@/lib/permissions'; import { useMemo, useCallback } from 'react'; import { ExtendedWorkspaceData } from '@/lib/types'; import SessionInsightsGrid from '@/components/workspace/SessionInsightsGrid'; import { PromptSettings } from '@/components/SessionResult/ResultTabs/components/PromptSettings'; import { toast } from 'hooks/use-toast'; -import * as db from '@/lib/db'; import { Loader2 } from 'lucide-react'; +import { useUpsertWorkspace } from '@/stores/SessionStore'; // Default visibility configuration for workspaces const defaultWorkspaceVisibilityConfig: ResultTabsVisibilityConfig = { @@ -70,30 +67,16 @@ export default function WorkspaceContent({ const canEdit = useMemo(() => hasMinimumRole('editor'), [hasMinimumRole]); - // Memoize the handleWorkspaceUpdate callback - const handleWorkspaceUpdate = useCallback((updates: Workspace) => { - // TODO check where this is used! - throw new ReferenceError("This method is not implemented yet; it should update the store directly where it's called. See second-last element in the stack.") - }, []); - + const upsertWorkspace = useUpsertWorkspace(); // Memoize the handlePromptChange callback const handlePromptChange = useCallback( async (newPrompt: string) => { + // This method usually also takes a 'type', + // but projects never have facilitation prompts, + // so no need to handle that! try { const updateData = { summary_prompt: newPrompt }; - const result = await db.updateWorkspace(workspaceId, updateData); - - if (result) { - // TODO check where this is used! - throw new ReferenceError("This method is not implemented yet; it should update the store directly where it's called. See second-last element in the stack.") - } else { - toast({ - title: 'Failed to update prompt', - description: - 'An error occurred while updating the prompt. Changes were not saved.', - variant: 'destructive', - }); - } + await upsertWorkspace.mutateAsync({ id: workspaceId, data: updateData }) } catch (error) { console.error('Failed to update prompt:', error); toast({ @@ -146,7 +129,6 @@ Here are some questions you might want to ask: initialGradientTo={extendedWorkspaceData.workspace?.gradientTo} initialUseGradient={extendedWorkspaceData.workspace?.useGradient} isEditable={isEditable} - onUpdate={handleWorkspaceUpdate} /> {isEditable && (
diff --git a/src/app/workspace/[w_id]/actions.ts b/src/app/workspace/[w_id]/actions.ts index 864cfcb7..e3560c78 100644 --- a/src/app/workspace/[w_id]/actions.ts +++ b/src/app/workspace/[w_id]/actions.ts @@ -2,17 +2,6 @@ import { put } from '@vercel/blob'; import * as db from '@/lib/db'; -import { WorkspaceUpdate } from '@/lib/schema'; - -export async function updateWorkspaceDetails(id: string, data: WorkspaceUpdate) { - try { - const workspaceOrNull = await db.upsertWorkspace(id, data); - return { success: workspaceOrNull != null }; - } catch (error) { - console.error('Error updating workspace details:', error); - return { success: false, error: 'Failed to update workspace details' }; - } -} export async function deleteWorkspace(id: string) { try { diff --git a/src/components/AddToProjectDialog.tsx b/src/components/AddToProjectDialog.tsx index 67df1a10..45de46f6 100644 --- a/src/components/AddToProjectDialog.tsx +++ b/src/components/AddToProjectDialog.tsx @@ -10,9 +10,7 @@ import { } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; -import { Label } from '@/components/ui/label'; import { - getAvailableWorkspaces, addSessionToWorkspaces, removeSessionFromWorkspace, } from '@/lib/sessionWorkspaceActions'; @@ -21,6 +19,8 @@ import { useRouter } from 'next/navigation'; import { FolderPlus, X } from 'lucide-react'; import { getWorkspacesForSession } from '@/lib/db'; +import { useAvailableWorkspaces } from '@/stores/SessionStore'; + interface AddToProjectDialogProps { sessionId: string; buttonClass?: string; @@ -39,8 +39,9 @@ export function AddToProjectDialog({ open, onOpenChange, }: AddToProjectDialogProps) { + const { data: availableWorkspacesToUser = [] } = useAvailableWorkspaces(); const [availableWorkspaces, setAvailableWorkspaces] = useState( - [] + availableWorkspacesToUser ); const [currentWorkspaces, setCurrentWorkspaces] = useState([]); const [selectedWorkspaces, setSelectedWorkspaces] = useState([]); @@ -71,16 +72,11 @@ export function AddToProjectDialog({ const loadWorkspaces = async () => { try { - // We need to modify the sessionWorkspaceActions.ts to add a function to get workspaces for a session - // For now, we'll assume we have this function - const allWorkspaces = await getAvailableWorkspaces(); - - // For this implementation, we'll need to add a new server action to get workspaces for a session - // This is a placeholder - we'll need to implement this function + // TODO: Use Store? const sessionWorkspaces = await getWorkspacesForSession(sessionId); // Filter out workspaces that the session is already part of - const availableWorkspacesFiltered = allWorkspaces.filter( + const availableWorkspacesFiltered = availableWorkspacesToUser.filter( (workspace) => !sessionWorkspaces.some((sw) => sw.id === workspace.id) ); diff --git a/src/components/SessionResult/ParticipantSessionRow.tsx b/src/components/SessionResult/ParticipantSessionRow.tsx index 06218acf..efa1811b 100644 --- a/src/components/SessionResult/ParticipantSessionRow.tsx +++ b/src/components/SessionResult/ParticipantSessionRow.tsx @@ -1,44 +1,11 @@ import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { TableCell, TableRow } from '@/components/ui/table'; -import { Message, SessionRating } from '@/lib/schema'; import { useEffect, useState } from 'react'; -import { ChatMessage } from '../ChatMessage'; -import { getAllChatMessagesInOrder, getThreadRating } from '@/lib/db'; import { ParticipantsTableData } from './SessionParticipantsTable'; -import { Spinner } from '../icons'; import { Switch } from '../ui/switch'; -import { MessageSquare, Star, X } from 'lucide-react'; -import * as db from '@/lib/db'; import { useUpsertUserSessions } from '@/stores/SessionStore'; - -const EMOJI_RATINGS = [ - { - emoji: '😫', - label: 'Very dissatisfied', - color: 'bg-red-50 text-red-700 border-red-200', - }, - { - emoji: '🙁', - label: 'Dissatisfied', - color: 'bg-orange-50 text-orange-700 border-orange-200', - }, - { - emoji: '😐', - label: 'Neutral', - color: 'bg-yellow-50 text-yellow-700 border-yellow-200', - }, - { - emoji: '🙂', - label: 'Satisfied', - color: 'bg-lime-50 text-lime-700 border-lime-200', - }, - { - emoji: '😊', - label: 'Very satisfied', - color: 'bg-green-50 text-green-700 border-green-200', - }, -]; +import TranscriptPopup from './TranscriptPopup'; export default function ParicipantSessionRow({ tableData, @@ -47,9 +14,6 @@ export default function ParicipantSessionRow({ }) { const userData = tableData.userData; const [isPopupVisible, setIsPopupVisible] = useState(false); - const [messages, setMessages] = useState([]); - const [rating, setRating] = useState(null); - const [isLoading, setIsLoading] = useState(false); const updateUserSession = useUpsertUserSessions(); const handleIncludeInSummaryUpdate = async (included: boolean) => { @@ -63,29 +27,6 @@ export default function ParicipantSessionRow({ const handleViewClick = async () => { setIsPopupVisible(true); - setIsLoading(true); - try { - const [messageHistory, threadRating] = await Promise.all([ - getAllChatMessagesInOrder(userData.thread_id), - getThreadRating(userData.thread_id), - ]); - - if (messageHistory.length === 0) { - console.error( - 'No messages found for ', - userData.thread_id, - ' but it should be there!', - ); - return; - } - - setMessages(messageHistory); - setRating(threadRating); - } catch (error) { - console.error('Error loading data:', error); - } finally { - setIsLoading(false); - } }; const handleCloseClick = () => { @@ -160,84 +101,13 @@ export default function ParicipantSessionRow({ - {isPopupVisible && ( -
-
e.stopPropagation()} - > -
-
-

- {tableData.userName}'s Conversation -

- {rating && ( -
-
- - {EMOJI_RATINGS[rating.rating - 1].emoji} - - {rating.rating}/5 - - - {EMOJI_RATINGS[rating.rating - 1].label} - -
-
- )} -
- -
- -
- {rating?.feedback && ( -
-
- -

- Participant's Feedback -

-
-

"{rating.feedback}"

-
- )} - {isLoading ? ( -
-
-
- -
-

- Loading conversation... -

-
-
- ) : ( -
- {messages.map((message, index) => ( - - ))} -
- )} -
-
-
- )} + {isPopupVisible && + + } ); } diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index c91033ad..6170f840 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Tabs } from '@radix-ui/react-tabs'; import { TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Card, CardContent } from '@/components/ui/card'; @@ -9,16 +9,13 @@ import SessionParticipantsTable from '../SessionParticipantsTable'; import SessionResultSummary from '../SessionResultSummary'; import { ChatMessage } from '../../ChatMessage'; import { - HostSession, ResultTabsVisibilityConfig, - UserSession, } from '@/lib/schema'; import { OpenAIMessage } from '@/lib/types'; -import { CirclePlusIcon, Loader, Loader2 } from 'lucide-react'; +import { CirclePlusIcon, Loader2 } from 'lucide-react'; import { CustomResponseCard } from './components/CustomResponseCard'; import { TabContent } from './components/TabContent'; import { useCustomResponses } from './hooks/useCustomResponses'; -import * as db from '@/lib/db'; import { ExportButton } from '@/components/Export/ExportButton'; import { usePermissions } from '@/lib/permissions'; import { @@ -246,37 +243,6 @@ export default function ResultTabs({ } }, [visibleTabs, activeTab]); - const handlePromptChange = async ( - newPrompt: string, - type: 'facilitation' | 'summary', - ) => { - try { - console.log('Updating prompt:', { type, newPrompt }); - - const updateData = - type === 'facilitation' - ? { prompt: newPrompt } - : { prompt_summary: newPrompt }; - - console.log('Update data:', updateData); - - await db.updateHostSession(resourceId, updateData); - - // Update the local state - if (hostData[0]) { - if (type === 'facilitation') { - hostData[0].prompt = newPrompt; - } else { - hostData[0].prompt_summary = newPrompt; - } - console.log('Updated hostData:', hostData[0]); - } - } catch (error) { - console.error('Failed to update prompt:', error); - throw error; - } - }; - // Message enhancement for chat const enhancedMessage = (message: OpenAIMessage, key: number) => { if ( diff --git a/src/components/SessionResult/TranscriptPopup.tsx b/src/components/SessionResult/TranscriptPopup.tsx new file mode 100644 index 00000000..2367f24a --- /dev/null +++ b/src/components/SessionResult/TranscriptPopup.tsx @@ -0,0 +1,137 @@ +import { SessionRating } from '@/lib/schema'; +import { X, MessageSquare, Loader2 } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { ChatMessage } from '../ChatMessage'; +import { Spinner } from '../icons'; +import { Button } from '../ui/button'; +import { useMessages } from '@/stores/SessionStore'; +import { getThreadRating } from '@/lib/db'; +import { ParticipantsTableData } from './SessionParticipantsTable'; + +const EMOJI_RATINGS = [ + { + emoji: '😫', + label: 'Very dissatisfied', + color: 'bg-red-50 text-red-700 border-red-200', + }, + { + emoji: '🙁', + label: 'Dissatisfied', + color: 'bg-orange-50 text-orange-700 border-orange-200', + }, + { + emoji: '😐', + label: 'Neutral', + color: 'bg-yellow-50 text-yellow-700 border-yellow-200', + }, + { + emoji: '🙂', + label: 'Satisfied', + color: 'bg-lime-50 text-lime-700 border-lime-200', + }, + { + emoji: '😊', + label: 'Very satisfied', + color: 'bg-green-50 text-green-700 border-green-200', + }, +]; + +export default async function TranscriptPopup({ + threadId, + handleCloseClick, + tableData, +}: { + threadId: string; + handleCloseClick: () => void; + tableData: ParticipantsTableData; +}) { + const [rating, setRating] = useState(null); + const { data: messages = [], isLoading } = useMessages(threadId); + + useEffect(() => { + getThreadRating(threadId).then((rating) => setRating(rating)); + }); + + if (isLoading) { + return ; + } + + return ( +
+
e.stopPropagation()} + > +
+
+

+ {tableData.userName}'s Conversation +

+ {rating && ( +
+
+ + {EMOJI_RATINGS[rating.rating - 1].emoji} + + {rating.rating}/5 + + - {EMOJI_RATINGS[rating.rating - 1].label} + +
+
+ )} +
+ +
+ +
+ {rating?.feedback && ( +
+
+ +

+ Participant's Feedback +

+
+

"{rating.feedback}"

+
+ )} + {isLoading ? ( +
+
+
+ +
+

Loading conversation...

+
+
+ ) : ( +
+ {messages.map((message, index) => ( + + ))} +
+ )} +
+
+
+ ); +} diff --git a/src/components/chat/ChatInterface.tsx b/src/components/chat/ChatInterface.tsx index 3eb00e12..a3fbe344 100644 --- a/src/components/chat/ChatInterface.tsx +++ b/src/components/chat/ChatInterface.tsx @@ -3,11 +3,11 @@ import Link from 'next/link'; import { HelpCircle, ChevronDown } from 'lucide-react'; import { FullscreenChat } from './FullscreenChat'; import { OpenAIMessage } from '@/lib/types'; -import { PoweredByHarmonica } from '@/components/icons'; import { useUser } from '@auth0/nextjs-auth0/client'; import { RatingModal } from './RatingModal'; import { useState, useEffect, useRef } from 'react'; -import { updateUserSession, increaseSessionsCount } from '@/lib/db'; +import { useUpsertUserSessions } from '@/stores/SessionStore'; +import { NewUserSession } from '@/lib/schema'; interface ChatInterfaceProps { hostData: { @@ -24,7 +24,6 @@ interface ChatInterfaceProps { isMounted: boolean; isLoading: boolean; message: OpenAIMessage; - assistantId?: string; userContext?: Record; questions?: JSON; } @@ -37,7 +36,6 @@ export const ChatInterface = ({ isMounted, isLoading, message, - assistantId, userContext, questions, }: ChatInterfaceProps) => { @@ -85,18 +83,19 @@ export const ChatInterface = ({ } }, [message?.is_final, threadId, message]); + const upsertUserSession = useUpsertUserSessions() + useEffect(() => { const updateSession = async () => { if (showRating && userSessionId) { try { - await new Promise((resolve) => setTimeout(resolve, 3000)); - - await updateUserSession(userSessionId, { + // What's the purpose of this? + // await new Promise((resolve) => setTimeout(resolve, 3000)); + await upsertUserSession.mutateAsync({ + id: userSessionId, active: false, last_edit: new Date(), - }); - - await increaseSessionsCount(userSessionId, 'num_finished'); + } as NewUserSession); } catch (error) { console.error('Error updating session:', error); } diff --git a/src/components/workspace/WorkspaceHero.tsx b/src/components/workspace/WorkspaceHero.tsx index 7faae6ab..27593fe3 100644 --- a/src/components/workspace/WorkspaceHero.tsx +++ b/src/components/workspace/WorkspaceHero.tsx @@ -12,10 +12,11 @@ import { Textarea } from '@/components/ui/textarea'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { useState, useCallback } from 'react'; -import { deleteWorkspace, updateWorkspaceDetails } from 'app/workspace/[w_id]/actions'; +import { deleteWorkspace } from 'app/workspace/[w_id]/actions'; import { useDropzone } from 'react-dropzone'; import { WorkspaceUpdate } from '@/lib/schema'; import { useRouter } from 'next/navigation'; +import { useUpsertWorkspace } from '@/stores/SessionStore'; interface WorkspaceHeroProps { workspaceId: string; @@ -28,7 +29,6 @@ interface WorkspaceHeroProps { initialGradientFrom?: string; initialGradientTo?: string; initialUseGradient?: boolean; - onUpdate?: (updates: any) => void; } export default function WorkspaceHero({ @@ -42,7 +42,6 @@ export default function WorkspaceHero({ initialGradientFrom, initialGradientTo, initialUseGradient, - onUpdate, }: WorkspaceHeroProps) { const [bannerImage, setBannerImage] = useState( bannerImageUrl @@ -55,6 +54,7 @@ export default function WorkspaceHero({ const [imageFile, setImageFile] = useState(null); const router = useRouter(); + const upsertWorkspace = useUpsertWorkspace(); const onDrop = useCallback((acceptedFiles: File[]) => { const file = acceptedFiles[0]; @@ -72,7 +72,7 @@ export default function WorkspaceHero({ } }, []); - const { getRootProps, getInputProps, isDragActive } = useDropzone({ + const { getInputProps } = useDropzone({ onDrop, accept: { 'image/*': [] }, maxFiles: 1, @@ -123,11 +123,7 @@ export default function WorkspaceHero({ setBannerImage(finalBannerImage); } - if (onUpdate) { - onUpdate(updateData); - } - - await updateWorkspaceDetails(workspaceId, updateData); + await upsertWorkspace.mutateAsync({ id: workspaceId, data: updateData }); } catch (error) { console.error('Error updating project:', error); } diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 67adf421..9d18e142 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -1,14 +1,13 @@ 'use client'; import { useEffect, useState, useRef } from 'react'; -import * as db from '@/lib/db'; import * as llama from '../app/api/llamaUtils'; import { OpenAIMessage, OpenAIMessageWithContext } from '@/lib/types'; import { UserProfile, useUser } from '@auth0/nextjs-auth0/client'; import { Message, NewUserSession } from '@/lib/schema'; import { getUserNameFromContext } from '@/lib/clientUtils'; -import { getUserSessionById, getAllChatMessagesInOrder } from '@/lib/db'; import { useInsertMessages, useMessages, useUpsertUserSessions, useUserSession } from '@/stores/SessionStore'; +import { getAllMessagesForUsersSorted } from '@/lib/db'; export interface UseChatOptions { sessionIds?: string[]; @@ -66,6 +65,7 @@ export function useChat(options: UseChatOptions) { const { data: existingUserSession, isLoading: isLoadingUserSession } = useUserSession(userSessionId || ''); const threadId = existingUserSession?.thread_id || ''; const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); + const upsertUserSessions = useUpsertUserSessions(); const placeholder = placeholderText ? placeholderText @@ -166,7 +166,11 @@ export function useChat(options: UseChatOptions) { console.log(`[i] Start creating thread`); const chatMessages = []; if (context?.userData) { - const allUsersMessages = await db.getAllMessagesForUsersSorted( + // Note: It is difficult to use SessionStore here, because hooks should only be called on the top level, + // but at that time we don't know yet whether we should even fetch these threads; + // and we can't have hooks in a loop anyway because react doesn't like that. + // So let's just stick with using the db directly here. + const allUsersMessages = await getAllMessagesForUsersSorted( context.userData, ); const messagesByThread = allUsersMessages.reduce( @@ -228,11 +232,11 @@ export function useChat(options: UseChatOptions) { created_at: new Date(), }); console.log('Inserting new session with initial data: ', data); - return db - .insertUserSessions(data) - .then((userIds) => { - if (userIds[0] && setUserSessionId) setUserSessionId(userIds[0]); - return userIds[0]; // Return the userId, just in case setUserSessionId is not fast enough + return upsertUserSessions.mutateAsync(data) + .then((users) => { + const userId = users[0]?.id; + if (userId && setUserSessionId) setUserSessionId(userId); + return userId; // Return the userId, just in case setUserSessionId is not fast enough }) .catch((error) => { console.error('[!] error creating user session -> ', error); diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index 0b5f67ab..bb03ed85 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -1,7 +1,7 @@ 'use client'; import { useEffect, useState } from 'react'; -import { useSummaryStatusOptimized, useUpdateSummary } from '@/stores/SessionStore'; +import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; export enum RefreshStatus { Unknown, @@ -38,7 +38,7 @@ export function useSummaryUpdateManager( isLoading, error, refetch - } = useSummaryStatusOptimized(resourceId, isProject); + } = useSummaryStatus(resourceId, isProject); const updateSummary = useUpdateSummary(); diff --git a/src/lib/db.ts b/src/lib/db.ts index 9f4ad3e4..0cd172e2 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -92,7 +92,7 @@ export async function getHostSessionsForIds( page: number = 1, pageSize: number = 100, ): Promise { - console.log('[i] Database Operation: getHostSessionsForIds'); + console.log('[i] Database Operation: getHostSessionsForIds', ids); const db = await dbPromise; try { @@ -143,7 +143,7 @@ export async function getFromHostSession( sessionId: string, columns: (keyof s.HostSessionsTable)[], ) { - console.log('[i] Database Operation: getFromHostSession'); + console.log('[i] Database Operation: getFromHostSession', sessionId); const db = await dbPromise; const result = await db .selectFrom(hostTableName) @@ -234,11 +234,12 @@ export async function updateHostSession( } } +// TODO: Do we still need this? Is it actually used? (I think it was broken at some point; and instead of relying on the db field, we're calculating it on the fly?) export async function increaseSessionsCount( id: string, toIncrease: 'num_sessions' | 'num_finished', ) { - console.log('[i] Database Operation: increaseSessionsCount'); + console.log('[i] Database Operation: increaseSessionsCount', id); // This is a bit clumsy, but I couldn't find a way with kysely to do it in one go. Submitting sql`...` breaks it :-( const db = await dbPromise; const previousNum = ( @@ -266,7 +267,7 @@ export async function getUsersBySessionId( sessionId: string, columns: (keyof s.UserSessionsTable)[] = [], ): Promise { - console.log('[i] Database Operation: getUsersBySessionId'); + console.log('[i] Database Operation: getUsersBySessionId', sessionId); try { const db = await dbPromise; let query = db @@ -354,7 +355,7 @@ export async function deleteUserSession(id: string): Promise { } export async function getUserSessionById(id: string): Promise { - console.log('[i] Database Operation: getUserSessionById'); + console.log('[i] Database Operation: getUserSessionById', id); try { const db = await dbPromise; return await db @@ -368,8 +369,6 @@ export async function getUserSessionById(id: string): Promise { + const session = await getSession(); + const userId = session?.user?.sub; + + if (!userId) { + return []; + } + + const resources = await db.getResourcesForUser(userId, 'WORKSPACE'); + if (!resources.length) return []; + + const workspaceIds = resources.map(resource => resource.resource_id); + return await db.getWorkspacesForIds(workspaceIds, ['id', 'title']); + }, + staleTime, + }); +} + // --- Mutations --- export function useUpsertWorkspace() { const queryClient = useQueryClient(); @@ -206,7 +227,7 @@ export function useUnlinkSessionFromWorkspace() { // --- Summary Operations --- -export function useSummaryStatusOptimized(sessionId: string, isProject = false) { +export function useSummaryStatus(sessionId: string, isProject = false) { const queryClient = useQueryClient(); return useQuery({ From b4967dde43da3cd2681517985e33dee801328389 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 10:42:27 +1200 Subject: [PATCH 10/27] Clean up Chat --- src/hooks/useChat.ts | 52 +++++--------------------------------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 9d18e142..94f799f4 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -135,18 +135,9 @@ export function useChat(options: UseChatOptions) { } }; - function concatenateMessages(messagesFromOneUser: Message[]) { - messagesFromOneUser.sort( - (a, b) => a.created_at.getTime() - b.created_at.getTime(), - ); - return messagesFromOneUser - .map((message) => `${message.role} : ${message.content}`) - .join('\n\n'); - } - /** - * Creates a new thread that will be used for the chat. - * @param context Initial message and context for the thread + * Create a new identifier for this chat. + * This would always only be called for facilitation sessions, not for AskAI. * @param sessionId Current session identifier * @param user User object * @param userName Optional display name @@ -154,7 +145,6 @@ export function useChat(options: UseChatOptions) { * @returns {Promise} The unique identifier for this **user** session */ async function createThread( - context: OpenAIMessageWithContext | undefined, sessionId: string | undefined, user: UserProfile | string, userName?: string, @@ -163,39 +153,7 @@ export function useChat(options: UseChatOptions) { if (isTesting) { return undefined; } - console.log(`[i] Start creating thread`); - const chatMessages = []; - if (context?.userData) { - // Note: It is difficult to use SessionStore here, because hooks should only be called on the top level, - // but at that time we don't know yet whether we should even fetch these threads; - // and we can't have hooks in a loop anyway because react doesn't like that. - // So let's just stick with using the db directly here. - const allUsersMessages = await getAllMessagesForUsersSorted( - context.userData, - ); - const messagesByThread = allUsersMessages.reduce( - (acc, message) => { - acc[message.thread_id] = acc[message.thread_id] || []; // to make sure this array exists - acc[message.thread_id].push(message); - return acc; - }, - {} as Record, - ); - chatMessages.push('\n----START CHAT HISTORY for CONTEXT----\n'); - const concatenatedUserMessages = Object.entries(messagesByThread).map( - ([threadId, messages]) => { - return `\n----START NEXT USER CHAT----\n${concatenateMessages( - messages, - )}\n----END USER CHAT----\n`; - }, - ); - chatMessages.push(...concatenatedUserMessages); - chatMessages.push('\n----END CHAT HISTORY for CONTEXT----\n'); - // console.log('[i] Chat messages for context: ', chatMessages); - } - const threadId = crypto.randomUUID(); - console.log(`[i] Created threadId ${threadId} for session ${sessionId}`); threadIdRef.current = threadId; if (onThreadIdReceived) { onThreadIdReceived(threadId); @@ -286,9 +244,11 @@ export function useChat(options: UseChatOptions) { } } else if (!userSessionId || (!existingUserSession && !isLoadingUserSession) || (!existingMessages && isLoadingMessages)) { // No existing session or session not found, create new thread + if (!sessionIds || sessionIds.length != 1) { + throw new Error('Cannot create a thread without a session ID or with multiple session IDs.'); + } createThread( - context, - sessionIds && sessionIds.length ? sessionIds[0] : undefined, + sessionIds[0], user ? user : 'id', userName, userContext, From 6ea9c35508f536a5e7654dbaadfa36c54447b285 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 11:13:03 +1200 Subject: [PATCH 11/27] More Pruning --- src/app/(dashboard)/sessions-table.tsx | 4 +- src/app/sessions/[id]/page.tsx | 18 ++--- src/app/workspace/[w_id]/page.tsx | 21 +++--- src/components/Export/ExportSection.tsx | 11 ++- .../SessionPage/SessionDataProvider.tsx | 66 ----------------- src/components/SessionPage/index.tsx | 50 +++++++------ .../SessionResult/ResultTabs/index.tsx | 1 - .../SessionResult/SessionResultHeader.tsx | 1 - .../SessionResult/SessionResultsOverview.tsx | 16 ++-- .../SessionResult/SessionResultsSection.tsx | 22 +----- .../SessionResult/TranscriptPopup.tsx | 8 +- src/hooks/useSummaryUpdater.ts | 0 src/lib/db.ts | 30 ++++---- src/lib/hooks/useSessionData.ts | 74 ------------------- src/lib/summaryActions.ts | 1 + src/stores/SessionStore.ts | 6 +- 16 files changed, 83 insertions(+), 246 deletions(-) delete mode 100644 src/components/SessionPage/SessionDataProvider.tsx delete mode 100644 src/hooks/useSummaryUpdater.ts delete mode 100644 src/lib/hooks/useSessionData.ts diff --git a/src/app/(dashboard)/sessions-table.tsx b/src/app/(dashboard)/sessions-table.tsx index 8a034bec..e311fd7a 100644 --- a/src/app/(dashboard)/sessions-table.tsx +++ b/src/app/(dashboard)/sessions-table.tsx @@ -5,7 +5,7 @@ import { Session } from './session'; import { Key, useEffect, useState } from 'react'; import SortableTable from '@/components/SortableTable'; import { HostSession } from '@/lib/schema'; -import { useWorkspaceStats } from '@/stores/SessionStore'; +import { useSessionsStats } from '@/stores/SessionStore'; import { getUserStats, SessionStatus } from '@/lib/clientUtils'; export type SessionTableData = { @@ -56,7 +56,7 @@ export function SessionsTable({ sessions }: { sessions: HostSession[] }) { const sessionIds = sessions.map((s) => s.id); - const { data: sessionToUserStats } = useWorkspaceStats(sessionIds); + const { data: sessionToUserStats } = useSessionsStats(sessionIds); useEffect(() => { if (sessionToUserStats) { diff --git a/src/app/sessions/[id]/page.tsx b/src/app/sessions/[id]/page.tsx index 762211b8..834eabef 100644 --- a/src/app/sessions/[id]/page.tsx +++ b/src/app/sessions/[id]/page.tsx @@ -1,11 +1,10 @@ import { Metadata } from 'next/dist/lib/metadata/types/metadata-interface'; import { getGeneratedMetadata } from 'app/api/metadata'; import { decryptId } from '@/lib/encryptionUtils'; -import SessionDataProvider from '@/components/SessionPage/SessionDataProvider'; -import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { QueryClient, HydrationBoundary, dehydrate } from '@tanstack/react-query'; import * as db from '@/lib/db'; import { checkSummaryNeedsUpdating, fetchSummary } from '@/lib/summaryActions'; +import SessionPage from '@/components/SessionPage'; // Increase the maximum execution time for this function on vercel export const maxDuration = 60; // in seconds @@ -24,11 +23,14 @@ export default async function SessionResult({ }) { const decryptedId = decryptId(params.id); const queryClient = new QueryClient(); + console.debug(new Error("Debugging Session Page").stack); // Prefetch all session-related data for optimal performance // (This is a server component, data is being prefetched on the server and then dehydrated, passed to the client and then further updates will happen there) // TanStack is taking care of the hydration magic. try { + console.log(`Prefetching data...`); + await Promise.allSettled([ // Prefetch host session data queryClient.prefetchQuery({ @@ -59,20 +61,10 @@ export default async function SessionResult({ console.warn('Failed to prefetch session data:', error); } - const visibilityConfig: ResultTabsVisibilityConfig = { - showSummary: true, - showResponses: true, - showCustomInsights: true, - showChat: true, - allowCustomInsightsEditing: true, - }; - return ( - ); diff --git a/src/app/workspace/[w_id]/page.tsx b/src/app/workspace/[w_id]/page.tsx index 0142ee4a..988e8ace 100644 --- a/src/app/workspace/[w_id]/page.tsx +++ b/src/app/workspace/[w_id]/page.tsx @@ -9,17 +9,17 @@ import * as db from '@/lib/db'; import { getSession } from '@auth0/nextjs-auth0'; // Increase the maximum execution time for this function on vercel -export const maxDuration = 300; // in seconds -export const revalidate = 0; // Disable caching for this page +// export const maxDuration = 300; // in seconds +// export const revalidate = 0; // Disable caching for this page -export async function generateMetadata({ - params, -}: { - params: { w_id: string }; -}): Promise { - return getGeneratedMetadata(`/workspace/${params.w_id}`); -} +// export async function generateMetadata({ +// params, +// }: { +// params: { w_id: string }; +// }): Promise { +// return getGeneratedMetadata(`/workspace/${params.w_id}`); +// } export default async function Workspace({ params, @@ -27,7 +27,8 @@ export default async function Workspace({ params: { w_id: string }; }) { const queryClient = new QueryClient(); - + console.log(`Call stack in Workspace page.tsx:`, new Error("Debugging Workspace Page").stack); + try { // First prefetch workspace data await queryClient.prefetchQuery({ diff --git a/src/components/Export/ExportSection.tsx b/src/components/Export/ExportSection.tsx index 80977717..666fa4c3 100644 --- a/src/components/Export/ExportSection.tsx +++ b/src/components/Export/ExportSection.tsx @@ -12,16 +12,13 @@ import { Button } from '../ui/button'; import { usePermissions } from '@/lib/permissions'; import { useCustomResponses } from '../SessionResult/ResultTabs/hooks/useCustomResponses'; import { OpenAIMessage } from '@/lib/types'; +import { useHostSession, useUserSessions } from '@/stores/SessionStore'; // ExportSection.tsx export default function ExportSection({ - hostData, - userData, id, className, }: { - hostData: HostSession; - userData: UserSession[]; id: string; className?: string; }) { @@ -29,6 +26,8 @@ export default function ExportSection({ const [exportInProgress, setExportInProgress] = useState(false); const [isExportPopupVisible, setIsExportPopupVisible] = useState(false); const [exportInstructions, setExportInstructions] = useState(''); + const { data: hostData } = useHostSession(id); + const { data: userData = []} = useUserSessions(id); const exportSessionResults = async (e: React.FormEvent) => { e.preventDefault(); @@ -44,7 +43,7 @@ export default function ExportSection({ type: 'application/json', }); const link = document.createElement('a'); - exportAndDownload(blob, link, `Harmonica_${hostData.topic ?? id}.json`); + exportAndDownload(blob, link, `Harmonica_${hostData?.topic ?? id}.json`); setExportInProgress(false); setIsExportPopupVisible(false); @@ -75,7 +74,7 @@ export default function ExportSection({ type: 'application/json', }), document.createElement('a'), - `Harmonica_${hostData.topic ?? id}_allData.json`, + `Harmonica_${hostData?.topic ?? id}_allData.json`, ); setExportInProgress(false); setIsExportPopupVisible(false); diff --git a/src/components/SessionPage/SessionDataProvider.tsx b/src/components/SessionPage/SessionDataProvider.tsx deleted file mode 100644 index d77feacd..00000000 --- a/src/components/SessionPage/SessionDataProvider.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Suspense } from 'react'; -import { fetchSessionData, SessionData } from '@/lib/hooks/useSessionData'; -import ErrorPage from '@/components/Error'; -import SessionPage from './index'; -import { OpenAIMessage } from '@/lib/types'; -import { ResultTabsVisibilityConfig } from '@/lib/schema'; - -interface SessionDataProviderProps { - sessionId: string; - workspaceId?: string; - visibilityConfig?: ResultTabsVisibilityConfig; - showShare?: boolean; - chatEntryMessage?: OpenAIMessage; -} - -async function SessionDataLoader({ - sessionId, - workspaceId, - visibilityConfig: defaultVisibilityConfig = { - showSummary: true, - showResponses: true, - showCustomInsights: true, - showChat: true, - allowCustomInsightsEditing: true, - showSessionRecap: true, - }, - ...props -}: SessionDataProviderProps) { - try { - const data = await fetchSessionData(sessionId, workspaceId); - - // Use persisted settings if available, otherwise use defaults - const visibilityConfig = data.visibilitySettings || defaultVisibilityConfig; - - return ( - - ); - } catch (error) { - console.error('Error loading session:', error); - - // If it's an access denied error, rethrow to use the error.tsx boundary - if (error instanceof Error && error.message.includes('Access denied')) { - throw error; - } - - // For other errors, use the inline error component - return ( - - ); - } -} - -export default function SessionDataProvider(props: SessionDataProviderProps) { - return ( - Loading session data...
}> - - - ); -} \ No newline at end of file diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index 563a372c..fb784d08 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -1,28 +1,26 @@ 'use client'; -import { SessionData } from '@/lib/hooks/useSessionData'; import SessionResultHeader from '@/components/SessionResult/SessionResultHeader'; import SessionResultsOverview from '@/components/SessionResult/SessionResultsOverview'; import SessionResultsSection from '@/components/SessionResult/SessionResultsSection'; -import { OpenAIMessage } from '@/lib/types'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; -import { SessionStatus } from '@/lib/clientUtils'; -import { useEffect } from 'react'; +import { getUserStats, SessionStatus } from '@/lib/clientUtils'; +import { useHostSession, useSessionsStats, useUserSessions } from '@/stores/SessionStore'; +import { Loader2 } from 'lucide-react'; -interface SessionPageProps { - data: SessionData; - visibilityConfig: ResultTabsVisibilityConfig; - showShare?: boolean; - chatEntryMessage?: OpenAIMessage; -} - -export default function SessionPage({ - data, - visibilityConfig, - showShare = true, - chatEntryMessage, -}: SessionPageProps) { - const { hostData, usersWithChat, stats } = data; +export default function SessionPage({ sessionId }: { sessionId: string }) { + const { data: hostData, isLoading } = useHostSession(sessionId) + const { data: userData } = useUserSessions(sessionId) + const { data: messageStats } = useSessionsStats([sessionId]) + if (isLoading || !hostData || !userData || !messageStats) { + return + } + + const usersWithChat = userData.filter( + (user) => messageStats[sessionId][user.id].num_messages > 2 + ); + console.log('message stats: ', messageStats, sessionId) + const stats = getUserStats(messageStats, sessionId) const status = !hostData.active || hostData.final_report_sent ? SessionStatus.FINISHED @@ -30,6 +28,17 @@ export default function SessionPage({ ? SessionStatus.DRAFT : SessionStatus.ACTIVE; + const visibilityConfig: ResultTabsVisibilityConfig = hostData.visibility_settings || { + showSummary: true, + showResponses: true, + showCustomInsights: true, + showChat: true, + allowCustomInsightsEditing: true, + showSessionRecap: true, + }; + + visibilityConfig.showChat = usersWithChat.length > 0 + return (
); diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 6170f840..e1aaa4e8 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -83,7 +83,6 @@ export default function ResultTabs({ const currentUserData = userData; - // Use the new TanStack Query-based summary update manager useSummaryUpdateManager(resourceId, { isProject, sessionIds, diff --git a/src/components/SessionResult/SessionResultHeader.tsx b/src/components/SessionResult/SessionResultHeader.tsx index 7755fd47..8839a709 100644 --- a/src/components/SessionResult/SessionResultHeader.tsx +++ b/src/components/SessionResult/SessionResultHeader.tsx @@ -21,7 +21,6 @@ export default function SessionResultHeader({ }: SessionResultHeaderProps) { const [isEditing, setIsEditing] = useState(false); const editableRef = useRef(null); - const { data: hostData } = useHostSession(sessionId); const upsertHostSession = useUpsertHostSession(); const { hasMinimumRole } = usePermissions(sessionId); const isEditable = hasMinimumRole('editor'); diff --git a/src/components/SessionResult/SessionResultsOverview.tsx b/src/components/SessionResult/SessionResultsOverview.tsx index 16411770..3a71ece3 100644 --- a/src/components/SessionResult/SessionResultsOverview.tsx +++ b/src/components/SessionResult/SessionResultsOverview.tsx @@ -11,7 +11,6 @@ export default function SessionResultsOverview({ startTime, numSessions, completedSessions, - showShare = true, currentPrompt, summaryPrompt, crossPollination, @@ -21,7 +20,6 @@ export default function SessionResultsOverview({ startTime: Date; numSessions: number; completedSessions: number; - showShare?: boolean; currentPrompt?: string; summaryPrompt?: string; crossPollination?: boolean; @@ -45,14 +43,12 @@ export default function SessionResultsOverview({ numSessions={numSessions} completedSessions={completedSessions} /> - {showShare && ( - - )} + ); } diff --git a/src/components/SessionResult/SessionResultsSection.tsx b/src/components/SessionResult/SessionResultsSection.tsx index 4be3b71a..9257e447 100644 --- a/src/components/SessionResult/SessionResultsSection.tsx +++ b/src/components/SessionResult/SessionResultsSection.tsx @@ -1,40 +1,29 @@ 'use client'; -import { HostSession, UserSession } from '@/lib/schema'; import React from 'react'; - import ResultTabs from './ResultTabs'; import ExportSection from '../Export/ExportSection'; -import { OpenAIMessage } from '@/lib/types'; import { ResultTabsVisibilityConfig } from '@/lib/schema'; import { usePermissions } from '@/lib/permissions'; import ShareSettings from '../ShareSettings'; export default function SessionResultsSection({ - hostData, - userData, resourceId, visibilityConfig, - chatEntryMessage, }: { - hostData: HostSession; - userData: UserSession[]; resourceId: string; visibilityConfig: ResultTabsVisibilityConfig; - showShare?: boolean; - chatEntryMessage?: OpenAIMessage; }) { const { loading, hasMinimumRole } = usePermissions(resourceId); - const hasMessages = userData.length > 0; - - visibilityConfig.showChat = visibilityConfig.showChat && hasMessages; + const showExport = visibilityConfig.showChat; // Show chat will be false if there aren't any messages yet, + // in which case we also don't need to show the export button. return ( <> {!loading && hasMinimumRole('editor') && (
@@ -43,12 +32,9 @@ export default function SessionResultsSection({ - {visibilityConfig.showResponses && hasMinimumRole('editor') && hasMessages && ( + {visibilityConfig.showResponses && hasMinimumRole('editor') && showExport && ( diff --git a/src/components/SessionResult/TranscriptPopup.tsx b/src/components/SessionResult/TranscriptPopup.tsx index 2367f24a..11e3e286 100644 --- a/src/components/SessionResult/TranscriptPopup.tsx +++ b/src/components/SessionResult/TranscriptPopup.tsx @@ -36,7 +36,7 @@ const EMOJI_RATINGS = [ }, ]; -export default async function TranscriptPopup({ +export default function TranscriptPopup({ threadId, handleCloseClick, tableData, @@ -50,11 +50,7 @@ export default async function TranscriptPopup({ useEffect(() => { getThreadRating(threadId).then((rating) => setRating(rating)); - }); - - if (isLoading) { - return ; - } + }, [threadId]); return (
{ - console.log('[i] Database Operation: getHostSessionsForIds', ids); + console.log('[i] Database Operation: getHostSessionsForIds', ids.length, 'ids'); const db = await dbPromise; try { @@ -109,11 +104,6 @@ export async function getHostSessionsForIds( .limit(pageSize) .offset(Math.max(0, page - 1) * pageSize); - // Log the SQL query - const sql = query.compile(); - console.log('SQL Query:', sql.sql); - console.log('SQL Parameters:', sql.parameters); - const result = await query.execute(); return result; } catch (error) { @@ -125,7 +115,8 @@ export async function getHostSessionsForIds( export async function getHostSessionById( sessionId: string, ): Promise { - console.log(`[i] Database Operation: getHostSessionById(${sessionId})`); + logCallerLocation('getHostSessionById', { sessionId }); + // console.log(`[i] Database Operation: getHostSessionById(${sessionId})`); const db = await dbPromise; try { return await db @@ -267,7 +258,8 @@ export async function getUsersBySessionId( sessionId: string, columns: (keyof s.UserSessionsTable)[] = [], ): Promise { - console.log('[i] Database Operation: getUsersBySessionId', sessionId); + logCallerLocation('getUsersBySessionId', { sessionId, columns }); + // console.log('[i] Database Operation: getUsersBySessionId', sessionId); try { const db = await dbPromise; let query = db @@ -1785,3 +1777,15 @@ export async function updateThreadRating( return null; } } + +function logCallerLocation(functionName: string, params?: any) { + // Only log stack trace if in development + if (process.env.NODE_ENV !== 'production') { + const err = new Error(); + // Only show stack after the first two lines + const splitStack = err.stack?.split('\n'); + const stack = splitStack?.slice(2, 6).filter((line) => !line.includes('node')); + console.log(`[trace] called with params:`, params); + console.debug(`Stack: `, stack); + } +} \ No newline at end of file diff --git a/src/lib/hooks/useSessionData.ts b/src/lib/hooks/useSessionData.ts deleted file mode 100644 index b8e580a2..00000000 --- a/src/lib/hooks/useSessionData.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as db from '@/lib/db'; -import { HostSession, UserSession } from '@/lib/schema'; -import { getUserStats } from '@/lib/clientUtils'; -import { ResultTabsVisibilityConfig } from '@/lib/schema'; -import { hasAccessToResource } from '@/lib/serverUtils'; - -export interface SessionData { - hostData: HostSession; - userData: UserSession[]; - stats: { - totalUsers: number; - finishedUsers: number; - }; - usersWithChat: UserSession[]; - visibilitySettings?: ResultTabsVisibilityConfig; -} - -export interface UseSessionDataProps { - sessionId: string; - workspaceId?: string; -} - -export async function fetchSessionData( - sessionId: string, - workspaceId?: string, -): Promise { - try { - // Check if user has permission to access this session - const hasAccess = await hasAccessToResource(sessionId); - if (!hasAccess) { - throw new Error('Access denied: You do not have permission to view this session'); - } - - // Fetch host session data - const hostData = await db.getHostSessionById(sessionId); - - // Fetch user data and stats - const [userData, messageStats] = await Promise.all([ - db.getUsersBySessionId(sessionId), - db.getNumUsersAndMessages([hostData.id]) - ]); - - // Filter users with sufficient chat messages - const usersWithChat = userData.filter( - (user) => messageStats[sessionId][user.id].num_messages > 2 - ); - - // Calculate user stats - const { totalUsers, finishedUsers } = getUserStats(messageStats, sessionId); - - // Get visibility settings from the appropriate source - let visibilitySettings: ResultTabsVisibilityConfig | undefined; - if (workspaceId) { - const workspaceData = await db.getWorkspaceById(workspaceId); - visibilitySettings = workspaceData?.visibility_settings; - } else { - visibilitySettings = hostData.visibility_settings; - } - - return { - hostData, - userData, - stats: { - totalUsers, - finishedUsers - }, - usersWithChat, - visibilitySettings - }; - } catch (error) { - console.error('Error fetching session data:', error); - throw error; - } -} \ No newline at end of file diff --git a/src/lib/summaryActions.ts b/src/lib/summaryActions.ts index 70a4545d..d05ddd71 100644 --- a/src/lib/summaryActions.ts +++ b/src/lib/summaryActions.ts @@ -4,6 +4,7 @@ import * as db from '@/lib/db'; import { checkSummaryAndMessageTimes } from '@/lib/clientUtils'; export async function checkSummaryNeedsUpdating(resourceId: string, isProject = false) { + // console.debug(new Error("Debug checkSummaryNeedsUpdating calls").stack); if (!resourceId) { throw new Error('resourceId is required'); } diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 23cfbe42..64886b1b 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -18,7 +18,7 @@ const summaryStatusKey = (sessionId: string) => ['summary-status', sessionId]; const summaryContentKey = (sessionId: string) => ['summary-content', sessionId]; // Workspace-specific query keys const workspaceSessionsKey = (workspaceId: string) => ['workspace-sessions', workspaceId]; -const workspaceStatsKey = (sessionIds: string[]) => ['workspace-stats', sessionIds.sort()]; +const sessionsStatsKey = (sessionIds: string[]) => ['workspace-stats', sessionIds.sort()]; const availableSessionsKey = () => ['available-sessions']; const staleTime = 1000 * 60; // 1 minute @@ -82,9 +82,9 @@ export function useWorkspaceSessionIds(workspaceId: string) { }); } -export function useWorkspaceStats(sessionIds: string[]) { +export function useSessionsStats(sessionIds: string[]) { return useQuery({ - queryKey: workspaceStatsKey(sessionIds), + queryKey: sessionsStatsKey(sessionIds), queryFn: () => db.getNumUsersAndMessages(sessionIds), enabled: sessionIds.length > 0, staleTime, From 4b2710a754edb8e5e74941ef2a28cf299a91b5ae Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 15:50:12 +1200 Subject: [PATCH 12/27] Fix Summary updates & include toggles --- src/app/actions/permissions.ts | 5 - src/app/test-chat/page.tsx | 7 - .../SessionResult/ExpandableWithExport.tsx | 4 +- .../SessionResult/ResultTabs/index.tsx | 8 +- .../SessionResult/SessionResultControls.tsx | 4 +- .../SessionResult/SessionResultSummary.tsx | 16 +- src/hooks/useSummaryUpdateManager.ts | 161 ++++++------------ src/lib/db.ts | 25 +-- src/lib/types.ts | 4 - src/stores/SessionStore.ts | 43 ++--- 10 files changed, 88 insertions(+), 189 deletions(-) diff --git a/src/app/actions/permissions.ts b/src/app/actions/permissions.ts index fdc206ab..20d776d3 100644 --- a/src/app/actions/permissions.ts +++ b/src/app/actions/permissions.ts @@ -23,8 +23,6 @@ export async function updateResourcePermission( error: 'Authentication required' }; } - - // TODO: Add permission check to ensure current user can modify permissions const result = await setPermission(resourceId, role, resourceType, userId); return { success: result }; @@ -55,7 +53,6 @@ export async function removeResourcePermission( }; } - // TODO: Add permission check to ensure current user can remove permissions // TODO: Add check to prevent removing the last owner const result = await removePermission(resourceId, userId, resourceType); @@ -139,8 +136,6 @@ export async function cancelInvitation( error: 'Authentication required' }; } - - // TODO: Add permission check to ensure current user can cancel invitations const result = await deleteInvitation(invitationId); return { success: result }; diff --git a/src/app/test-chat/page.tsx b/src/app/test-chat/page.tsx index ebe59faf..27b47cef 100644 --- a/src/app/test-chat/page.tsx +++ b/src/app/test-chat/page.tsx @@ -9,15 +9,11 @@ import Link from 'next/link'; import { Loader2, HelpCircle } from 'lucide-react'; import { getHostSessionById, - increaseSessionsCount, - updateHostSession, updateUserSession, } from '@/lib/db'; -import { sql } from 'kysely'; import { useUser } from '@auth0/nextjs-auth0/client'; import { OpenAIMessage } from '@/lib/types'; import { encryptId } from '@/lib/encryptionUtils'; -import { PoweredByHarmonica } from '@/components/icons'; const StandaloneChat = () => { const [message, setMessage] = useState({ @@ -70,9 +66,6 @@ Please type your name or "anonymous" if you prefer active: false, last_edit: new Date(), }) - .then(() => { - increaseSessionsCount(sessionId!, 'num_finished'); - }) .then(() => { setIsLoading(false); setUserFinished(true); diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index 8f1b829b..a8cb3f49 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -14,6 +14,7 @@ import { RefreshStatus, useSummaryUpdateManager } from '@/hooks/useSummaryUpdate interface CardProps { resourceId: string; + sessionIds?: string[], title: string; content?: string; isExpanded: boolean; @@ -47,6 +48,7 @@ const StatusIndicator = ({ status }: { status: RefreshStatus | undefined }) => { export const ExpandableWithExport = ({ resourceId, + sessionIds, title, content, isExpanded, @@ -57,7 +59,7 @@ export const ExpandableWithExport = ({ loading, className, }: CardProps) => { - const summaryManager = useSummaryUpdateManager(resourceId); + const summaryManager = useSummaryUpdateManager(resourceId, sessionIds); return ( { diff --git a/src/components/SessionResult/SessionResultControls.tsx b/src/components/SessionResult/SessionResultControls.tsx index 61d27473..d21d11b0 100644 --- a/src/components/SessionResult/SessionResultControls.tsx +++ b/src/components/SessionResult/SessionResultControls.tsx @@ -34,7 +34,6 @@ interface SessionResultControlsProps { currentPrompt?: string; summaryPrompt?: string; crossPollination?: boolean; - sessionTopic?: string; } export default function SessionResultControls({ @@ -44,7 +43,6 @@ export default function SessionResultControls({ currentPrompt = '', summaryPrompt = '', crossPollination = true, - sessionTopic = '', }: SessionResultControlsProps) { const [loadSummary, setLoadSummary] = useState(false); const [isCloning, setIsCloning] = useState(false); @@ -132,7 +130,7 @@ export default function SessionResultControls({ const updateSummary = async () => { setLoadSummary(true); try { - await updateSummaryMutation.mutateAsync({ sessionId: id }); + await updateSummaryMutation.mutateAsync({ resourceId: id }); } finally { setLoadSummary(false); } diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index ba986039..422d4a9c 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -1,12 +1,12 @@ 'use client' import { HostSession } from '@/lib/schema'; -import { useEffect, useState } from 'react'; -import { RefreshStatus } from '@/hooks/useSummaryUpdateManager'; +import { useState } from 'react'; import { useUpdateSummary } from '@/stores/SessionStore'; import { ExpandableWithExport } from './ExpandableWithExport'; import { Card, CardContent } from '../ui/card'; import { usePermissions } from '@/lib/permissions'; import { useSummary } from '@/hooks/useSummary'; +import { useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; interface SessionResultSummaryProps { hostData: HostSession[]; @@ -31,20 +31,15 @@ export default function SessionResultSummary({ const resourceId: string = isProject ? projectId! : hostData[0].id; const { hasMinimumRole } = usePermissions(resourceId); - const updateSummaryMutation = useUpdateSummary(); - + const summaryUpdateManager = useSummaryUpdateManager(resourceId, isProject ? hostData.map(h => h.id) : undefined) // Use SWR to fetch summary content with initial data as fallback const initialSummary = isProject ? '' : hostData[0]?.summary || ''; const { data: summary, isLoading: summaryLoading } = useSummary(resourceId, initialSummary, isProject); const manuallyTriggerSummaryUpdate = async () => { setIsUpdating(true); - try { - await updateSummaryMutation.mutateAsync({ - sessionId: hostData[0].id, - isProject, - projectId - }); + try { + await summaryUpdateManager.startUpdateNow() } finally { setIsUpdating(false); } @@ -72,6 +67,7 @@ export default function SessionResultSummary({ {showSummaryContent ? ( h.id) : undefined} title={isProject ? "Project Summary" : "Session Summary"} content={summary} isExpanded={isExpandedSummary} diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index bb03ed85..cfb58669 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -1,100 +1,78 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; export enum RefreshStatus { Unknown, Outdated, UpdatePending, + UpdateStarted, UpToDate, } -interface UseUpdateManagerOptions { - isProject?: boolean; - sessionIds?: string[]; - projectId?: string; - source?: 'participants' | 'chat' | 'ui' | 'auto'; - userSessionId?: string; - enablePolling?: boolean; - pollingInterval?: number; -} - export function useSummaryUpdateManager( - resourceId: string, - options: UseUpdateManagerOptions = {} + resourceId: string, + sessionIds?: string[], // Provided for projects ) { - const { - isProject = false, - sessionIds, - projectId - } = options; - - const [lastEditTimestamp, setLastEditTimestamp] = useState(); - + + const [status, setStatus] = useState(RefreshStatus.Unknown) + // Use optimized summary status that leverages cached data - const { - data: summaryStatus, - isLoading, - error, - refetch - } = useSummaryStatus(resourceId, isProject); - - const updateSummary = useUpdateSummary(); - - // Calculate current status - const status = (() => { - if (isLoading || !summaryStatus) return RefreshStatus.Unknown; - - const delay = 30000; // 30 seconds - const now = Date.now(); - - if (summaryStatus.lastEdit > summaryStatus.lastSummaryUpdate) { - if (now - summaryStatus.lastEdit > delay && !updateSummary.isPending) { - return RefreshStatus.UpdatePending; + const { + data: summaryStatus, + isLoading, + error + } = useSummaryStatus(resourceId, sessionIds && sessionIds.length > 0); + + const summaryUpdater = useUpdateSummary(); + const timeoutIdRef = useRef(null); + + useEffect(() => { + // Clear any existing timeout when dependencies change or component unmounts + return () => { + if (timeoutIdRef.current) { + clearTimeout(timeoutIdRef.current); } - return RefreshStatus.Outdated; - } - - return RefreshStatus.UpToDate; - })(); + }; + }, []); // Empty dependency array ensures this cleanup runs only on unmount - // Auto-trigger updates when status becomes UpdatePending useEffect(() => { - if (status === RefreshStatus.UpdatePending && - summaryStatus && - summaryStatus.lastEdit !== lastEditTimestamp) { - - console.log(`[useSummaryUpdateManager] Auto-triggering update for ${resourceId}`); - - updateSummary.mutate({ - sessionId: resourceId, - isProject, - sessionIds, - projectId - }); - - setLastEditTimestamp(summaryStatus.lastEdit); + if (isLoading || !summaryStatus) { + setStatus(RefreshStatus.Unknown); + return; } - }, [status, summaryStatus, lastEditTimestamp, resourceId, isProject, sessionIds, projectId, updateSummary]); - // Manual update function - const updateNow = async () => { - if (updateSummary.isPending) { + const delay = 30000; // 30 seconds + + if (summaryStatus.lastEdit > summaryStatus.lastSummaryUpdate) { + console.log(`[useSummaryUpdateManager] Registered some edit. Schedule summary update for ${resourceId}`); + // We only get here if there was some sort of edit. So let's reschedule any previously scheduled update + if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current); + + setStatus(RefreshStatus.UpdatePending); + timeoutIdRef.current = setTimeout(() => { + startUpdateNow(); + }, delay); // Calculate remaining time + } else { + setStatus(RefreshStatus.UpToDate); + } + }, [summaryStatus, resourceId, sessionIds]); + + const startUpdateNow = async () => { + if (summaryUpdater.isPending || status === RefreshStatus.UpdateStarted) { console.log(`[useSummaryUpdateManager] Update already running for ${resourceId}`); return; } + setStatus(RefreshStatus.UpdateStarted); + console.log(`[useSummaryUpdateManager] Update triggered for ${resourceId}`); - console.log(`[useSummaryUpdateManager] Manual update triggered for ${resourceId}`); - try { - const result = await updateSummary.mutateAsync({ - sessionId: resourceId, - isProject, + const result = await summaryUpdater.mutateAsync({ + resourceId, sessionIds, - projectId }); - + console.log(`[useSummaryUpdateManager] Update completed for ${resourceId}`); return result; } catch (error) { @@ -103,50 +81,21 @@ export function useSummaryUpdateManager( } }; - // Polling control - const startPolling = () => { - console.log(`[useSummaryUpdateManager] Starting polling for ${resourceId}`); - // TanStack Query handles polling automatically via refetchInterval - // We could force a refetch here if needed - refetch(); - }; - - const stopPolling = () => { - console.log(`[useSummaryUpdateManager] Stopping polling for ${resourceId}`); - // TanStack Query polling is controlled by the query options - // Individual queries can be paused, but it's handled at query level - }; - return { // State status, - isRunning: updateSummary.isPending, + isRunning: summaryUpdater.isPending, isLoading, - error: error || updateSummary.error, + error: error || summaryUpdater.error, lastEditTimestamp: summaryStatus?.lastEdit, version: summaryStatus ? { lastEdit: summaryStatus.lastEdit, lastSummaryUpdate: summaryStatus.lastSummaryUpdate } : undefined, - - // Actions - updateNow, - startPolling, - stopPolling, - refetch, - - // Compatibility with old SummaryUpdateManager interface - getState: () => ({ - isRunning: updateSummary.isPending, - lastEditTimestamp: summaryStatus?.lastEdit, - status, - version: summaryStatus ? { - lastEdit: summaryStatus.lastEdit, - lastSummaryUpdate: summaryStatus.lastSummaryUpdate - } : undefined, - listeners: new Set(), // Not used in React hook version - }), - + + // Actions to trigger updates manually + startUpdateNow, + // React-specific subscribe: () => { // In React hooks, subscriptions are handled by the hook itself diff --git a/src/lib/db.ts b/src/lib/db.ts index 123490aa..b3a3b21c 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -225,24 +225,6 @@ export async function updateHostSession( } } -// TODO: Do we still need this? Is it actually used? (I think it was broken at some point; and instead of relying on the db field, we're calculating it on the fly?) -export async function increaseSessionsCount( - id: string, - toIncrease: 'num_sessions' | 'num_finished', -) { - console.log('[i] Database Operation: increaseSessionsCount', id); - // This is a bit clumsy, but I couldn't find a way with kysely to do it in one go. Submitting sql`...` breaks it :-( - const db = await dbPromise; - const previousNum = ( - await db - .selectFrom(hostTableName) - .where('id', '=', id) - .select(toIncrease) - .executeTakeFirstOrThrow() - )[toIncrease]; - updateHostSession(id, { [toIncrease]: previousNum + 1 }); -} - export async function deleteHostSession(id: string): Promise { console.log('[i] Database Operation: deleteHostSession'); try { @@ -974,7 +956,8 @@ export async function setPermission( userId = session?.user?.sub; } if (!userId) { - console.warn('Could not get user info. Will store session as anonymous.'); + console.warn('Could not get user info; not setting permissions!'); + return false; } const db = await dbPromise; @@ -991,7 +974,7 @@ export async function setPermission( .insertInto(permissionsTableName) .values({ resource_id: sessionId, - user_id: userId || 'anonymous', + user_id: userId!, role, resource_type: 'SESSION', }) @@ -1010,7 +993,7 @@ export async function setPermission( .insertInto(permissionsTableName) .values({ resource_id: resourceId, - user_id: userId || 'anonymous', + user_id: userId, role, resource_type: resourceType, }) diff --git a/src/lib/types.ts b/src/lib/types.ts index 9469183a..1fe996f1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,7 +1,3 @@ -// TODO: Clean up types to make them more concise and intuitive. -// There's just too much going on right now and the separation between -// UserData, SessionData & AccumulatedData isn't clear enough. - import { UserSession, Message, diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 64886b1b..4fa35caa 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -227,28 +227,23 @@ export function useUnlinkSessionFromWorkspace() { // --- Summary Operations --- -export function useSummaryStatus(sessionId: string, isProject = false) { +export function useSummaryStatus(resourceId: string, isProject = false) { const queryClient = useQueryClient(); return useQuery({ - queryKey: summaryStatusKey(sessionId), + queryKey: summaryStatusKey(resourceId), queryFn: async () => { // For projects, we need different caching strategy if (isProject) { // For workspaces, check if we have workspace data cached - const cachedWorkspace = queryClient.getQueryData(workspaceKey(sessionId)); - if (cachedWorkspace) { - // We have workspace data, but still need to call server action - // for complex multi-session timestamp calculation - return checkSummaryNeedsUpdating(sessionId, true); - } + // const cachedWorkspace = queryClient.getQueryData(workspaceKey(sessionId)); // No workspace cache, fallback to server action - return checkSummaryNeedsUpdating(sessionId, true); + return checkSummaryNeedsUpdating(resourceId, true); } // For sessions, try to use cached data first to avoid database calls - const cachedHost = queryClient.getQueryData(hostKey(sessionId)); - const cachedUsers = queryClient.getQueryData(userKey(sessionId)); + const cachedHost = queryClient.getQueryData(hostKey(resourceId)); + const cachedUsers = queryClient.getQueryData(userKey(resourceId)); if (cachedHost && cachedUsers) { const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUsers); @@ -260,14 +255,14 @@ export function useSummaryStatus(sessionId: string, isProject = false) { return { lastEdit: Math.max(lastMessage, lastUserEdit), lastSummaryUpdate, - resourceId: sessionId + resourceId: resourceId }; } // Fallback to server action if no cached data - return checkSummaryNeedsUpdating(sessionId, false); + return checkSummaryNeedsUpdating(resourceId, false); }, - enabled: !!sessionId, + enabled: !!resourceId, staleTime: 10000, refetchInterval: 10000, }); @@ -285,28 +280,26 @@ export function useSummaryContent(sessionId: string) { export function useUpdateSummary() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ sessionId, isProject = false, sessionIds, projectId }: { - sessionId: string; - isProject?: boolean; + mutationFn: ({ resourceId, sessionIds }: { + resourceId: string; sessionIds?: string[]; - projectId?: string; }) => { - if (isProject) { + if (sessionIds && sessionIds.length > 1) { return createMultiSessionSummary( - sessionIds?.length ? sessionIds : [sessionId], - projectId ?? sessionId + sessionIds, + resourceId ); } else { - return createSummary(sessionId); + return createSummary(resourceId); } }, onSuccess: (summary, variables) => { // Update cache with new summary content - queryClient.setQueryData(summaryContentKey(variables.sessionId), summary); + queryClient.setQueryData(summaryContentKey(variables.resourceId), summary); // Invalidate status to reflect that summary is now up to date - queryClient.invalidateQueries({ queryKey: summaryStatusKey(variables.sessionId) }); + queryClient.invalidateQueries({ queryKey: summaryStatusKey(variables.resourceId) }); // Invalidate host session to update last_summary_update timestamp - queryClient.invalidateQueries({ queryKey: hostKey(variables.sessionId) }); + queryClient.invalidateQueries({ queryKey: hostKey(variables.resourceId) }); }, }); } From 98c1897b604f99032fc2ef982903f157b9a60e5f Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 20:03:21 +1200 Subject: [PATCH 13/27] Fix SummaryUpdateManager --- .../SessionResult/ExpandableWithExport.tsx | 1 + .../SessionResult/ResultTabs/index.tsx | 8 +- .../SessionResult/SessionResultSummary.tsx | 17 +- src/hooks/useChat.ts | 1 - src/hooks/useSummaryUpdateManager.ts | 163 ++++++++++++------ src/lib/summaryActions.ts | 1 - 6 files changed, 124 insertions(+), 67 deletions(-) diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index a8cb3f49..7ca70692 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -34,6 +34,7 @@ const StatusIndicator = ({ status }: { status: RefreshStatus | undefined }) => { case RefreshStatus.Outdated: return 'bg-red-500'; case RefreshStatus.UpdatePending: + case RefreshStatus.UpdateStarted: return 'bg-yellow-500 animate-pulse'; case RefreshStatus.Unknown: default: diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index e86a2bf5..2e7cd5ff 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -83,7 +83,12 @@ export default function ResultTabs({ const currentUserData = userData; - useSummaryUpdateManager(resourceId, sessionIds); + // Declaring this here so that we don't redeclare it whenever the summary tab gets loaded + const summaryUpdateManager = useSummaryUpdateManager(resourceId, sessionIds); + const manuallyTriggerSummaryUpdate = async () => { + await summaryUpdateManager.startUpdateNow() + }; + // Define available tabs and their visibility conditions in one place const availableTabs = useMemo(() => { @@ -106,6 +111,7 @@ export default function ResultTabs({ (hasMinimumRole('editor') || visibilityConfig.showSummary) ?? true } showSessionRecap={visibilityConfig.showSessionRecap ?? true} + onSummaryUpdateTrigger={manuallyTriggerSummaryUpdate} /> ), }, diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index 422d4a9c..267d3be1 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -1,12 +1,10 @@ 'use client' import { HostSession } from '@/lib/schema'; import { useState } from 'react'; -import { useUpdateSummary } from '@/stores/SessionStore'; import { ExpandableWithExport } from './ExpandableWithExport'; import { Card, CardContent } from '../ui/card'; import { usePermissions } from '@/lib/permissions'; import { useSummary } from '@/hooks/useSummary'; -import { useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; interface SessionResultSummaryProps { hostData: HostSession[]; @@ -15,6 +13,7 @@ interface SessionResultSummaryProps { draft: boolean; showSummary?: boolean; showSessionRecap?: boolean; + onSummaryUpdateTrigger?: () => void; } export default function SessionResultSummary({ @@ -24,6 +23,7 @@ export default function SessionResultSummary({ draft = false, showSummary = true, showSessionRecap = true, + onSummaryUpdateTrigger }: SessionResultSummaryProps) { const [isUpdating, setIsUpdating] = useState(false); const [isExpandedPrompt, setIsExpandedPrompt] = useState(false); @@ -31,20 +31,11 @@ export default function SessionResultSummary({ const resourceId: string = isProject ? projectId! : hostData[0].id; const { hasMinimumRole } = usePermissions(resourceId); - const summaryUpdateManager = useSummaryUpdateManager(resourceId, isProject ? hostData.map(h => h.id) : undefined) + // Use SWR to fetch summary content with initial data as fallback const initialSummary = isProject ? '' : hostData[0]?.summary || ''; const { data: summary, isLoading: summaryLoading } = useSummary(resourceId, initialSummary, isProject); - const manuallyTriggerSummaryUpdate = async () => { - setIsUpdating(true); - try { - await summaryUpdateManager.startUpdateNow() - } finally { - setIsUpdating(false); - } - }; - // Check which content will be shown const showSessionRecapContent = showSessionRecap && !isProject && hostData[0]?.prompt_summary; const showSummaryContent = showSummary && summary; @@ -73,7 +64,7 @@ export default function SessionResultSummary({ isExpanded={isExpandedSummary} onExpandedChange={setIsExpandedSummary} showRefreshButton={hasMinimumRole('editor')} - onRefresh={manuallyTriggerSummaryUpdate} + onRefresh={onSummaryUpdateTrigger} loading={summaryLoading || isUpdating} /> ) : showDraftProjectCard ? ( diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 94f799f4..e45f771d 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -209,7 +209,6 @@ export function useChat(options: UseChatOptions) { } useEffect(() => { - console.log('[i] User context: ', userContext); if ( userContext && !threadIdRef.current && diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index cfb58669..3d2c0b9d 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -1,7 +1,8 @@ 'use client'; -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useRef, useCallback, useState } from 'react'; import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; +import { create } from 'zustand'; export enum RefreshStatus { Unknown, @@ -11,96 +12,156 @@ export enum RefreshStatus { UpToDate, } +// Define a global store for managing active updates, +// so that we don't schedule multiple updates from different components +interface UpdateManagerStore { + // Add status for each resourceId + status: Record; + lastRegisteredEdit: Record; + // Add actions to update status + setStatus: (resourceId: string, status: RefreshStatus) => void; + setLastRegisteredEdit: (resourceId: string, lastEdit: number) => void; +} + +const useUpdateManagerStore = create((set) => ({ + status: {}, + lastRegisteredEdit: {}, + setStatus: (resourceId, status) => + set((state) => { + console.debug(`[UpdateManagerStore] Setting status for ${resourceId} to ${Object.keys(RefreshStatus).filter(k => isNaN(Number(k)))[status]}`); + if (state.status[resourceId] === status) return state; // No change if status is the same; necessary to prevent infinite rerender loops + return ({ + status: { ...state.status, [resourceId]: status }, + }) + }), + setLastRegisteredEdit: (resourceId, lastEdit) => + set((state) => ({ lastRegisteredEdit: { ...state.lastRegisteredEdit, [resourceId]: lastEdit }})) +})); + export function useSummaryUpdateManager( resourceId: string, - sessionIds?: string[], // Provided for projects + sessionIds?: string[] // Provided for projects ) { - const [status, setStatus] = useState(RefreshStatus.Unknown) - // Use optimized summary status that leverages cached data + // This will be the main source of truth for incoming edits, + // the only other thing that should trigger an update is manually. const { data: summaryStatus, isLoading, - error + error, } = useSummaryStatus(resourceId, sessionIds && sessionIds.length > 0); const summaryUpdater = useUpdateSummary(); const timeoutIdRef = useRef(null); + const { status, setStatus, lastRegisteredEdit, setLastRegisteredEdit } = useUpdateManagerStore(); useEffect(() => { - // Clear any existing timeout when dependencies change or component unmounts return () => { if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); } }; - }, []); // Empty dependency array ensures this cleanup runs only on unmount - - useEffect(() => { - if (isLoading || !summaryStatus) { - setStatus(RefreshStatus.Unknown); + }, [resourceId]); + + const startUpdateNow = useCallback(async () => { + // Prevent starting if already running globally or if this instance thinks it's started + if ( + summaryUpdater.isPending || + status[resourceId] === RefreshStatus.UpdateStarted + ) { + console.debug(`[useSummaryUpdateManager] Update already running for ${resourceId}.`); return; } - const delay = 30000; // 30 seconds - - if (summaryStatus.lastEdit > summaryStatus.lastSummaryUpdate) { - console.log(`[useSummaryUpdateManager] Registered some edit. Schedule summary update for ${resourceId}`); - // We only get here if there was some sort of edit. So let's reschedule any previously scheduled update - if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current); - - setStatus(RefreshStatus.UpdatePending); - timeoutIdRef.current = setTimeout(() => { - startUpdateNow(); - }, delay); // Calculate remaining time - } else { - setStatus(RefreshStatus.UpToDate); - } - }, [summaryStatus, resourceId, sessionIds]); - - const startUpdateNow = async () => { - if (summaryUpdater.isPending || status === RefreshStatus.UpdateStarted) { - console.log(`[useSummaryUpdateManager] Update already running for ${resourceId}`); - return; - } - setStatus(RefreshStatus.UpdateStarted); - console.log(`[useSummaryUpdateManager] Update triggered for ${resourceId}`); + setStatus(resourceId, RefreshStatus.UpdateStarted); + console.debug(`[useSummaryUpdateManager] Update triggered for ${resourceId}`); try { const result = await summaryUpdater.mutateAsync({ resourceId, sessionIds, }); - console.log(`[useSummaryUpdateManager] Update completed for ${resourceId}`); + console.debug(`[useSummaryUpdateManager] Update completed for ${resourceId}`); return result; } catch (error) { - console.error(`[useSummaryUpdateManager] Update failed for ${resourceId}:`, error); + console.error( + `[useSummaryUpdateManager] Update failed for ${resourceId}:`, + error + ); throw error; + } finally { + setStatus(resourceId, RefreshStatus.UpToDate); } - }; + }, [ + resourceId, + sessionIds, + summaryUpdater, + setStatus, + status[resourceId], + ]); + + // Effect to schedule the summary update + useEffect(() => { + if (isLoading || !summaryStatus) { + console.debug(`[useSummaryUpdateManager] No summary status or loading for ${resourceId}`); + setStatus(resourceId, RefreshStatus.Unknown); + return; // Do nothing if loading or no summary status + } + + // Only schedule if no update is currently in progress globally + if (status[resourceId] === RefreshStatus.UpdateStarted) { + console.debug(`[useSummaryUpdateManager] Update already started for ${resourceId}`); + return; // Don't schedule if an update is already running + } + + if (summaryStatus.lastSummaryUpdate > summaryStatus.lastEdit) { + setStatus(resourceId, RefreshStatus.UpToDate); + return; + } + + const delay = 30000; // 30 seconds + console.debug( + `[useSummaryUpdateManager] lastRegisteredEdit for ${resourceId}: ${lastRegisteredEdit[resourceId] ?? 0}` + ) + if (summaryStatus.lastEdit > (lastRegisteredEdit[resourceId] ?? 0)) { + console.debug( + `[useSummaryUpdateManager] Registered some edit. Schedule summary update for ${resourceId}. +Last edit: ${summaryStatus.lastEdit} +Last summary update: ${summaryStatus.lastSummaryUpdate}` + ); + + setLastRegisteredEdit(resourceId, summaryStatus.lastEdit); + + // Reset existing timers + if (timeoutIdRef.current) { + console.debug(`[useSummaryUpdateManager] Replacing existing timeout for ${resourceId}`); + clearTimeout(timeoutIdRef.current); + } + + // Schedule the update + timeoutIdRef.current = setTimeout(() => { + startUpdateNow(); + }, delay); + setStatus(resourceId, RefreshStatus.UpdatePending); + } + }, [ + startUpdateNow, + summaryStatus, + isLoading, + resourceId, + setStatus, + setLastRegisteredEdit, + ]); return { // State - status, - isRunning: summaryUpdater.isPending, + status: status[resourceId] || RefreshStatus.Unknown, isLoading, error: error || summaryUpdater.error, - lastEditTimestamp: summaryStatus?.lastEdit, - version: summaryStatus ? { - lastEdit: summaryStatus.lastEdit, - lastSummaryUpdate: summaryStatus.lastSummaryUpdate - } : undefined, // Actions to trigger updates manually startUpdateNow, - - // React-specific - subscribe: () => { - // In React hooks, subscriptions are handled by the hook itself - // Return a no-op cleanup function for compatibility - return () => {}; - }, }; } \ No newline at end of file diff --git a/src/lib/summaryActions.ts b/src/lib/summaryActions.ts index d05ddd71..70a4545d 100644 --- a/src/lib/summaryActions.ts +++ b/src/lib/summaryActions.ts @@ -4,7 +4,6 @@ import * as db from '@/lib/db'; import { checkSummaryAndMessageTimes } from '@/lib/clientUtils'; export async function checkSummaryNeedsUpdating(resourceId: string, isProject = false) { - // console.debug(new Error("Debug checkSummaryNeedsUpdating calls").stack); if (!resourceId) { throw new Error('resourceId is required'); } From c08987f30d324da22b72f399281ef3c00f86691a Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 11:08:55 +1200 Subject: [PATCH 14/27] Fix upsert --- .../workspace/SessionInsightsGrid.tsx | 15 ++-------- src/hooks/useChat.ts | 12 ++++---- src/lib/db.ts | 30 +++---------------- src/stores/SessionStore.ts | 13 +++++--- 4 files changed, 21 insertions(+), 49 deletions(-) diff --git a/src/components/workspace/SessionInsightsGrid.tsx b/src/components/workspace/SessionInsightsGrid.tsx index 0834b2c9..2c53e0eb 100644 --- a/src/components/workspace/SessionInsightsGrid.tsx +++ b/src/components/workspace/SessionInsightsGrid.tsx @@ -1,10 +1,10 @@ import { Card, CardContent, CardHeader } from '@/components/ui/card'; import SessionSummaryCard from '@/components/SessionResult/SessionSummaryCard'; -import { HostSession, UserSession } from '@/lib/schema'; +import { HostSession } from '@/lib/schema'; import { Button } from '@/components/ui/button'; -import { LinkIcon, Loader, Pencil, Plus } from 'lucide-react'; +import { LinkIcon, Loader, Plus } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import { useEffect, useState, useCallback } from 'react'; +import { useState } from 'react'; import { Dialog, DialogContent, @@ -15,17 +15,8 @@ import { } from '@/components/ui/dialog'; import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; -import { - linkSessionsToWorkspace, - unlinkSessionFromWorkspace, -} from '@/lib/workspaceActions'; import { useToast } from 'hooks/use-toast'; -import * as db from '@/lib/db'; import { - useUpsertWorkspace, - useUpsertHostSession, - useUpsertUserSessions, - useHostSession, useWorkspace, useLinkSessionsToWorkspace, useUnlinkSessionFromWorkspace, diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index e45f771d..d4557880 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -4,10 +4,9 @@ import { useEffect, useState, useRef } from 'react'; import * as llama from '../app/api/llamaUtils'; import { OpenAIMessage, OpenAIMessageWithContext } from '@/lib/types'; import { UserProfile, useUser } from '@auth0/nextjs-auth0/client'; -import { Message, NewUserSession } from '@/lib/schema'; +import { NewUserSession } from '@/lib/schema'; import { getUserNameFromContext } from '@/lib/clientUtils'; import { useInsertMessages, useMessages, useUpsertUserSessions, useUserSession } from '@/stores/SessionStore'; -import { getAllMessagesForUsersSorted } from '@/lib/db'; export interface UseChatOptions { sessionIds?: string[]; @@ -191,10 +190,9 @@ export function useChat(options: UseChatOptions) { }); console.log('Inserting new session with initial data: ', data); return upsertUserSessions.mutateAsync(data) - .then((users) => { - const userId = users[0]?.id; - if (userId && setUserSessionId) setUserSessionId(userId); - return userId; // Return the userId, just in case setUserSessionId is not fast enough + .then((newUserIds) => { + if (newUserIds && newUserIds.length == 1 && setUserSessionId) setUserSessionId(newUserIds[0]); + return newUserIds[0]; // Return the userId, just in case setUserSessionId is not fast enough }) .catch((error) => { console.error('[!] error creating user session -> ', error); @@ -363,7 +361,7 @@ export function useChat(options: UseChatOptions) { created_at: now, }), userSessionId || userSessionIdFromThread - ? useUpsertUserSessions().mutateAsync({ + ? upsertUserSessions.mutateAsync({ id: userSessionId || userSessionIdFromThread!, last_edit: now, } as NewUserSession) // It is not a _new_ user session, but this type allows us to omit all the other fields! diff --git a/src/lib/db.ts b/src/lib/db.ts index b3a3b21c..be596619 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -274,43 +274,21 @@ export async function insertUserSessions( } } -export async function upsertUserSessions( - data: s.NewUserSession | s.NewUserSession[], - onConflict: 'skip' | 'update' = 'update', -): Promise { - console.log('[i] Database Operation: upsertUserSessions'); - try { - const db = await dbPromise; - const result = await db - .insertInto(userTableName) - .values(data) - .onConflict((oc) => - onConflict === 'skip' - ? oc.column('id').doNothing() - : oc.column('id').doUpdateSet(data), - ) - .returningAll() - .execute(); - return result; - } catch (error) { - console.error('Error upserting user session:', error); - throw error; - } -} - export async function updateUserSession( id: string, data: s.UserSessionUpdate, -): Promise { +): Promise { console.log('[i] Database Operation: updateUserSession'); try { const db = await dbPromise; console.log('Updating user session with id:', id, ' with data:', data); - await db + const result = await db .updateTable(userTableName) .set(data) .where('id', '=', id) + .returningAll() .execute(); + return result.map((row) => row.id); } catch (error) { console.error('Error updating user session:', error); throw error; diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 4fa35caa..3048720d 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -158,11 +158,16 @@ export function useUpsertHostSession() { export function useUpsertUserSessions() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (data: NewUserSession | NewUserSession[]) => db.upsertUserSessions(data), - onSuccess: (result, _input) => { + mutationFn: (data: NewUserSession) => { + if (!data.id) { + return db.insertUserSessions(data); + } else { + return db.updateUserSession(data.id, data) + } + }, + onSuccess: (results, _input) => { // Invalidate all affected session queries so that if we query for a session it will be fetched - const sessionIds = result.map(v => v.session_id); - sessionIds.forEach(sessionId => { + results.forEach(sessionId => { queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); }); }, From 5521fa7f01bef2a4997176b16a8aa2b734beb35d Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 14:47:15 +1200 Subject: [PATCH 15/27] Fix chat empty message --- src/app/api/llamaUtils.ts | 1 + src/components/SessionPage/index.tsx | 2 +- src/components/chat/ChatMessages.tsx | 11 ++++++++++- src/hooks/useChat.ts | 9 ++++++--- src/hooks/useSummaryUpdateManager.ts | 3 ++- src/stores/SessionStore.ts | 9 ++++++--- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/app/api/llamaUtils.ts b/src/app/api/llamaUtils.ts index cb6b18b8..c0cd0787 100644 --- a/src/app/api/llamaUtils.ts +++ b/src/app/api/llamaUtils.ts @@ -60,6 +60,7 @@ export async function handleGenerateAnswer( ? await getAllChatMessagesInOrder(messageData.threadId) : []; + console.log(`[i] Fetched messages:\n`, messages); // Skip cross-pollination logic entirely if it's disabled if (crossPollinationEnabled) { // Only attempt cross-pollination if there are enough messages and we're not in skip mode diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index fb784d08..72229ae5 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -17,7 +17,7 @@ export default function SessionPage({ sessionId }: { sessionId: string }) { } const usersWithChat = userData.filter( - (user) => messageStats[sessionId][user.id].num_messages > 2 + (user) => messageStats[sessionId][user.id].num_messages ?? 0 > 2 ); console.log('message stats: ', messageStats, sessionId) const stats = getUserStats(messageStats, sessionId) diff --git a/src/components/chat/ChatMessages.tsx b/src/components/chat/ChatMessages.tsx index 8849208f..5d51fc99 100644 --- a/src/components/chat/ChatMessages.tsx +++ b/src/components/chat/ChatMessages.tsx @@ -2,6 +2,7 @@ import { OpenAIMessage } from '@/lib/types'; import { ChatMessage } from '../ChatMessage'; +import { useEffect, useRef } from 'react'; interface ChatMessagesProps { chat: { @@ -32,8 +33,16 @@ export function ChatMessages({ errorToastMessage, messagesEndRef, } = chat; + + const scrollPanelRef = useRef(null); + useEffect(() => { + if (scrollPanelRef?.current && messages.length > 1) { + scrollPanelRef.current.scrollTop = scrollPanelRef.current.scrollHeight; + } + }, [messages, scrollPanelRef]); + return ( -
+
{messages.map((message, index) => (
{customMessageEnhancement ? ( diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index d4557880..91a0378e 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -301,7 +301,10 @@ export function useChat(options: UseChatOptions) { console.log( `[i] Inserting chat message for user session ${userSessionId}`, ); - messageInserter.mutate({ + // Need to do await here, because we process this chat on the server + // and we need to wait here until it actually is written to the database; + // i.e. optimistic client-side updates won't be enough! + await messageInserter.mutateAsync({ thread_id: threadIdRef.current, role: 'user', content: messageText, @@ -337,11 +340,11 @@ export function useChat(options: UseChatOptions) { messageText, sessionId: sessionIds?.[0] || '', }; - + console.log(`[i] Generating answer for message: ${messageText}`); llama .handleGenerateAnswer( messageData, - !isAskAi && sessionIds?.length === 1 && crossPollination, + sessionIds?.length === 1 && crossPollination, ) .then((answer) => { if (answer.is_final && setShowRating) { diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index 3d2c0b9d..21ca24fd 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -66,6 +66,7 @@ export function useSummaryUpdateManager( const startUpdateNow = useCallback(async () => { // Prevent starting if already running globally or if this instance thinks it's started + console.debug(`[useSummaryUpdateManager] startUpdateNow called for ${resourceId} with status: ${status[resourceId]}`); if ( summaryUpdater.isPending || status[resourceId] === RefreshStatus.UpdateStarted @@ -76,7 +77,7 @@ export function useSummaryUpdateManager( setStatus(resourceId, RefreshStatus.UpdateStarted); - console.debug(`[useSummaryUpdateManager] Update triggered for ${resourceId}`); + console.debug(`[useSummaryUpdateManager] Update triggered for ${resourceId} from `, new Error().stack); try { const result = await summaryUpdater.mutateAsync({ resourceId, diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 3048720d..aa13dafd 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -238,6 +238,7 @@ export function useSummaryStatus(resourceId: string, isProject = false) { return useQuery({ queryKey: summaryStatusKey(resourceId), queryFn: async () => { + console.log(`[i] Using SummaryStatus for resourceId: ${resourceId}`); // For projects, we need different caching strategy if (isProject) { // For workspaces, check if we have workspace data cached @@ -256,16 +257,18 @@ export function useSummaryStatus(resourceId: string, isProject = false) { const lastEditTime = new Date(user.last_edit).getTime(); return lastEditTime > latest ? lastEditTime : latest; }, 0); - + console.log(`[i] Cached Summary status: Last Message: ${lastMessage}, Last Summary Update: ${lastSummaryUpdate}, Last User Edit: ${lastUserEdit}`); return { lastEdit: Math.max(lastMessage, lastUserEdit), lastSummaryUpdate, resourceId: resourceId }; } - + // Fallback to server action if no cached data - return checkSummaryNeedsUpdating(resourceId, false); + const results = await checkSummaryNeedsUpdating(resourceId, false); + console.log(`[i] Server Summary status: Last ediy: ${results.lastEdit}, Last Summary Update: ${results.lastSummaryUpdate}`); + return results; }, enabled: !!resourceId, staleTime: 10000, From bb49ad2cb84d825cc8a1e1390b999e3d85db8ac7 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 17:08:46 +1200 Subject: [PATCH 16/27] Half baked summary update fix --- src/hooks/useChat.ts | 25 +++++++++++++----------- src/stores/SessionStore.ts | 39 +++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 91a0378e..2ee7d4b7 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -65,6 +65,7 @@ export function useChat(options: UseChatOptions) { const threadId = existingUserSession?.thread_id || ''; const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); const upsertUserSessions = useUpsertUserSessions(); + const messageInserter = useInsertMessages(); const placeholder = placeholderText ? placeholderText @@ -86,7 +87,6 @@ export function useChat(options: UseChatOptions) { }; const [isLoading, setIsLoading] = useState(false); - const messageInserter = useInsertMessages(); const textareaRef = useRef(null); const messagesEndRef = useRef(null); const [isParticipantSuggestionLoading, setIsParticipantSuggestionLoading] = @@ -191,7 +191,9 @@ export function useChat(options: UseChatOptions) { console.log('Inserting new session with initial data: ', data); return upsertUserSessions.mutateAsync(data) .then((newUserIds) => { - if (newUserIds && newUserIds.length == 1 && setUserSessionId) setUserSessionId(newUserIds[0]); + if (newUserIds && newUserIds.length == 1 && setUserSessionId) { + setUserSessionId(newUserIds[0]); + } return newUserIds[0]; // Return the userId, just in case setUserSessionId is not fast enough }) .catch((error) => { @@ -297,9 +299,11 @@ export function useChat(options: UseChatOptions) { await waitForThreadCreation(threadIdRef, setErrorMessage); } - if (userSessionId && !isAutomatic && !isAskAi) { + const currentUserSessionId = userSessionId || userSessionIdFromThread; + + if (currentUserSessionId && !isAutomatic && !isAskAi) { console.log( - `[i] Inserting chat message for user session ${userSessionId}`, + `[i] Inserting chat message for user session ${currentUserSessionId}`, ); // Need to do await here, because we process this chat on the server // and we need to wait here until it actually is written to the database; @@ -356,19 +360,18 @@ export function useChat(options: UseChatOptions) { const now = new Date(); addMessage(answer); - if (userSessionId || userSessionIdFromThread) { + console.log(`Got reply; updating messages and upserting user session: ${currentUserSessionId}`); + if (currentUserSessionId) { Promise.all([ messageInserter.mutateAsync({ ...answer, thread_id: threadIdRef.current, created_at: now, }), - userSessionId || userSessionIdFromThread - ? upsertUserSessions.mutateAsync({ - id: userSessionId || userSessionIdFromThread!, - last_edit: now, - } as NewUserSession) // It is not a _new_ user session, but this type allows us to omit all the other fields! - : Promise.resolve(), + upsertUserSessions.mutateAsync({ + id: currentUserSessionId, + last_edit: now, + } as NewUserSession) // This will now trigger invalidation of the host session's user list ]).catch((error) => { console.log( 'Error storing answer or updating last edit: ', diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index aa13dafd..411157c8 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -44,12 +44,12 @@ export function useHostSession(sessionId: string) { }); } -export function useUserSessions(sessionId: string) { +export function useUserSessions(hostSessionId: string) { return useQuery({ - queryKey: userKey(sessionId), - queryFn: () => db.getUsersBySessionId(sessionId), + queryKey: userKey(hostSessionId), + queryFn: () => db.getUsersBySessionId(hostSessionId), select: (data) => data ?? [], - enabled: !!sessionId, + enabled: !!hostSessionId, staleTime, }); } @@ -158,17 +158,22 @@ export function useUpsertHostSession() { export function useUpsertUserSessions() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (data: NewUserSession) => { + mutationFn: async (data: NewUserSession) => { if (!data.id) { - return db.insertUserSessions(data); + return await db.insertUserSessions(data); } else { - return db.updateUserSession(data.id, data) + return await db.updateUserSession(data.id, data); } }, - onSuccess: (results, _input) => { - // Invalidate all affected session queries so that if we query for a session it will be fetched - results.forEach(sessionId => { - queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); + onSuccess: (result, _input) => { + result.forEach(userSessionId => { + queryClient.invalidateQueries({ queryKey: userKey(userSessionId) }); + }); + // Also invalidate summary status for the host session, as user edits affect it + userSessions.forEach(userSession => { + if (userSession.host_session_id) { + queryClient.invalidateQueries({ queryKey: summaryStatusKey(userSession.host_session_id) }); + } }); }, }); @@ -238,7 +243,6 @@ export function useSummaryStatus(resourceId: string, isProject = false) { return useQuery({ queryKey: summaryStatusKey(resourceId), queryFn: async () => { - console.log(`[i] Using SummaryStatus for resourceId: ${resourceId}`); // For projects, we need different caching strategy if (isProject) { // For workspaces, check if we have workspace data cached @@ -249,15 +253,16 @@ export function useSummaryStatus(resourceId: string, isProject = false) { // For sessions, try to use cached data first to avoid database calls const cachedHost = queryClient.getQueryData(hostKey(resourceId)); - const cachedUsers = queryClient.getQueryData(userKey(resourceId)); + const cachedUsersForResource = queryClient.getQueryData(userKey(resourceId)); + const cachedUsersIndividual = cachedUsersForResource?.map(user => queryClient.getQueryData(userKey(user.id))); - if (cachedHost && cachedUsers) { - const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUsers); - const lastUserEdit = cachedUsers.reduce((latest: number, user: UserSession) => { + if (cachedHost && cachedUsersForResource) { + const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUsersForResource); + const lastUserEdit = cachedUsersForResource.reduce((latest: number, user: UserSession) => { const lastEditTime = new Date(user.last_edit).getTime(); return lastEditTime > latest ? lastEditTime : latest; }, 0); - console.log(`[i] Cached Summary status: Last Message: ${lastMessage}, Last Summary Update: ${lastSummaryUpdate}, Last User Edit: ${lastUserEdit}`); + console.log(`[i] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); return { lastEdit: Math.max(lastMessage, lastUserEdit), lastSummaryUpdate, From 336e9446a217358a70dde49b00d897034743fda0 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 20:32:45 +1200 Subject: [PATCH 17/27] Fix bakery --- src/app/chat/StandaloneChat.tsx | 4 +- .../SessionResult/ParticipantSessionRow.tsx | 4 +- src/components/chat/ChatInterface.tsx | 4 +- src/hooks/useChat.ts | 4 +- src/stores/SessionStore.ts | 142 +++++++++++------- 5 files changed, 96 insertions(+), 62 deletions(-) diff --git a/src/app/chat/StandaloneChat.tsx b/src/app/chat/StandaloneChat.tsx index 843230bb..f46b1bdb 100644 --- a/src/app/chat/StandaloneChat.tsx +++ b/src/app/chat/StandaloneChat.tsx @@ -8,7 +8,7 @@ import { LoadingOverlay } from '@/components/chat/LoadingOverlay'; import { SessionModal } from '@/components/chat/SessionModal'; import { ChatInterface } from '@/components/chat/ChatInterface'; import { QuestionInfo } from 'app/create/types'; -import { useHostSession, useUpsertUserSessions } from '@/stores/SessionStore'; +import { useHostSession, useUpsertUserSession } from '@/stores/SessionStore'; import { NewUserSession } from '@/lib/schema'; const StandaloneChat = () => { @@ -48,7 +48,7 @@ Please type your name or "anonymous" if you prefer return {}; }); - const upsertUserSession = useUpsertUserSessions(); + const upsertUserSession = useUpsertUserSession(); useEffect(() => { setIsMounted(true); diff --git a/src/components/SessionResult/ParticipantSessionRow.tsx b/src/components/SessionResult/ParticipantSessionRow.tsx index efa1811b..9d0d41a1 100644 --- a/src/components/SessionResult/ParticipantSessionRow.tsx +++ b/src/components/SessionResult/ParticipantSessionRow.tsx @@ -4,7 +4,7 @@ import { TableCell, TableRow } from '@/components/ui/table'; import { useEffect, useState } from 'react'; import { ParticipantsTableData } from './SessionParticipantsTable'; import { Switch } from '../ui/switch'; -import { useUpsertUserSessions } from '@/stores/SessionStore'; +import { useUpsertUserSession } from '@/stores/SessionStore'; import TranscriptPopup from './TranscriptPopup'; export default function ParicipantSessionRow({ @@ -14,7 +14,7 @@ export default function ParicipantSessionRow({ }) { const userData = tableData.userData; const [isPopupVisible, setIsPopupVisible] = useState(false); - const updateUserSession = useUpsertUserSessions(); + const updateUserSession = useUpsertUserSession(); const handleIncludeInSummaryUpdate = async (included: boolean) => { // Updates the store & db diff --git a/src/components/chat/ChatInterface.tsx b/src/components/chat/ChatInterface.tsx index a3fbe344..d4635ea1 100644 --- a/src/components/chat/ChatInterface.tsx +++ b/src/components/chat/ChatInterface.tsx @@ -6,7 +6,7 @@ import { OpenAIMessage } from '@/lib/types'; import { useUser } from '@auth0/nextjs-auth0/client'; import { RatingModal } from './RatingModal'; import { useState, useEffect, useRef } from 'react'; -import { useUpsertUserSessions } from '@/stores/SessionStore'; +import { useUpsertUserSession } from '@/stores/SessionStore'; import { NewUserSession } from '@/lib/schema'; interface ChatInterfaceProps { @@ -83,7 +83,7 @@ export const ChatInterface = ({ } }, [message?.is_final, threadId, message]); - const upsertUserSession = useUpsertUserSessions() + const upsertUserSession = useUpsertUserSession() useEffect(() => { const updateSession = async () => { diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 2ee7d4b7..30990b7e 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -6,7 +6,7 @@ import { OpenAIMessage, OpenAIMessageWithContext } from '@/lib/types'; import { UserProfile, useUser } from '@auth0/nextjs-auth0/client'; import { NewUserSession } from '@/lib/schema'; import { getUserNameFromContext } from '@/lib/clientUtils'; -import { useInsertMessages, useMessages, useUpsertUserSessions, useUserSession } from '@/stores/SessionStore'; +import { useInsertMessages, useMessages, useUpsertUserSession, useUserSession } from '@/stores/SessionStore'; export interface UseChatOptions { sessionIds?: string[]; @@ -64,7 +64,7 @@ export function useChat(options: UseChatOptions) { const { data: existingUserSession, isLoading: isLoadingUserSession } = useUserSession(userSessionId || ''); const threadId = existingUserSession?.thread_id || ''; const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); - const upsertUserSessions = useUpsertUserSessions(); + const upsertUserSessions = useUpsertUserSession(); const messageInserter = useInsertMessages(); const placeholder = placeholderText diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 411157c8..ad002b90 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,4 +1,4 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { useQuery, useMutation, useQueryClient, useQueries } from '@tanstack/react-query'; import * as db from '@/lib/db'; import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace, HostSession, UserSession, User } from '@/lib/schema'; import { fetchWorkspaceData } from '@/lib/workspaceData'; @@ -10,12 +10,17 @@ import { getSession } from '@auth0/nextjs-auth0'; // --- Query Keys --- -const workspaceKey = (id: string) => ['workspace', id]; -const hostKey = (id: string) => ['host', id]; -const userKey = (sessionId: string) => ['users', sessionId]; -const messageKey = (threadId: string) => ['messages', threadId]; +const workspaceObjectKey = (id: string) => ['workspace-objects', id]; +const hostObjectKey = (id: string) => ['host-session-objects', id]; +const userObjectKey = (sessionId: string) => ['user-session-objects', sessionId]; + +// --- Mapping of hostIds -> userIds --- +const hostUserIdsKey = (hostId: string) => ['host-user-ids', hostId]; + +const messagesKey = (threadId: string) => ['messages', threadId]; const summaryStatusKey = (sessionId: string) => ['summary-status', sessionId]; const summaryContentKey = (sessionId: string) => ['summary-content', sessionId]; + // Workspace-specific query keys const workspaceSessionsKey = (workspaceId: string) => ['workspace-sessions', workspaceId]; const sessionsStatsKey = (sessionIds: string[]) => ['workspace-stats', sessionIds.sort()]; @@ -26,7 +31,7 @@ const staleTime = 1000 * 60; // 1 minute export function useWorkspace(workspaceId: string) { const queryClient = useQueryClient(); return useQuery({ - queryKey: workspaceKey(workspaceId), + queryKey: workspaceObjectKey(workspaceId), queryFn: () => fetchWorkspaceData(workspaceId, queryClient), select: (data) => data ?? [], // Returns an empty array if data isn't available yet enabled: !!workspaceId, @@ -36,7 +41,7 @@ export function useWorkspace(workspaceId: string) { export function useHostSession(sessionId: string) { return useQuery({ - queryKey: hostKey(sessionId), + queryKey: hostObjectKey(sessionId), queryFn: () => db.getHostSessionById(sessionId), select: (data) => data ?? [], enabled: !!sessionId, @@ -45,18 +50,39 @@ export function useHostSession(sessionId: string) { } export function useUserSessions(hostSessionId: string) { - return useQuery({ - queryKey: userKey(hostSessionId), - queryFn: () => db.getUsersBySessionId(hostSessionId), - select: (data) => data ?? [], + // First, get the user IDs for this host session + const userIdsQuery = useQuery({ + queryKey: hostUserIdsKey(hostSessionId), + queryFn: async () => { + const users = await db.getUsersBySessionId(hostSessionId); + return users.map(user => user.id); + }, enabled: !!hostSessionId, staleTime, }); + + // Then, get the full user entities + const userEntitiesQueries = useQueries({ + queries: (userIdsQuery.data || []).map(userId => ({ + queryKey: userObjectKey(userId), + queryFn: () => db.getUserSessionById(userId), + enabled: !!userId, + staleTime, + })) + }); + + // Combine the results + return { + data: userEntitiesQueries.map(query => query.data).filter(Boolean) as UserSession[], + isLoading: userIdsQuery.isLoading || userEntitiesQueries.some(query => query.isLoading), + isError: userIdsQuery.isError || userEntitiesQueries.some(query => query.isError), + error: userIdsQuery.error || userEntitiesQueries.find(query => query.error)?.error, + }; } export function useUserSession(userSessionId: string) { return useQuery({ - queryKey: ['user-session', userSessionId], + queryKey: userObjectKey(userSessionId), queryFn: () => db.getUserSessionById(userSessionId), enabled: !!userSessionId, staleTime, @@ -65,7 +91,7 @@ export function useUserSession(userSessionId: string) { export function useMessages(threadId: string) { return useQuery({ - queryKey: messageKey(threadId), + queryKey: messagesKey(threadId), queryFn: () => db.getAllChatMessagesInOrder(threadId), enabled: !!threadId, }); @@ -140,7 +166,7 @@ export function useUpsertWorkspace() { mutationFn: ({ id, data }: { id: string; data: Partial }) => db.upsertWorkspace(id, data), onSuccess: (_data, variables) => { - queryClient.invalidateQueries({ queryKey: workspaceKey(variables.id) }); + queryClient.invalidateQueries({ queryKey: workspaceObjectKey(variables.id) }); }, }); } @@ -150,31 +176,32 @@ export function useUpsertHostSession() { return useMutation({ mutationFn: (data: NewHostSession) => db.upsertHostSession(data, 'update'), onSuccess: (result, _data) => { - queryClient.invalidateQueries({ queryKey: hostKey(result.id) }); + queryClient.invalidateQueries({ queryKey: hostObjectKey(result.id) }); }, }); } -export function useUpsertUserSessions() { +export function useUpsertUserSession() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (data: NewUserSession) => { if (!data.id) { - return await db.insertUserSessions(data); + return (await db.insertUserSessions(data))[0]; } else { - return await db.updateUserSession(data.id, data); + return (await db.updateUserSession(data.id, data))[0]; } }, - onSuccess: (result, _input) => { - result.forEach(userSessionId => { - queryClient.invalidateQueries({ queryKey: userKey(userSessionId) }); - }); - // Also invalidate summary status for the host session, as user edits affect it - userSessions.forEach(userSession => { - if (userSession.host_session_id) { - queryClient.invalidateQueries({ queryKey: summaryStatusKey(userSession.host_session_id) }); - } - }); + onSuccess: (userSessionId, input) => { + // Invalidate individual user entity caches + queryClient.invalidateQueries({ queryKey: userObjectKey(userSessionId) }); + + // Get host session ID from input data or from local cache to invalidate host-user mapping + const hostSessionId = input.session_id + || queryClient.getQueryData(userObjectKey(userSessionId))?.session_id; + if (hostSessionId) { + queryClient.invalidateQueries({ queryKey: hostUserIdsKey(hostSessionId) }); + queryClient.invalidateQueries({ queryKey: summaryStatusKey(hostSessionId) }); + } }, }); } @@ -184,7 +211,7 @@ export function useInsertMessages() { return useMutation({ mutationFn: (message: NewMessage) => db.insertChatMessage(message), onSuccess: (_result, input) => { - queryClient.invalidateQueries({ queryKey: messageKey(input.thread_id) }); + queryClient.invalidateQueries({ queryKey: messagesKey(input.thread_id) }); }, }); } @@ -194,12 +221,12 @@ export function useRemoveSession() { return useMutation({ mutationFn: (sessionId: string) => db.deleteSessionById(sessionId), onSuccess: (_data, sessionId) => { - queryClient.invalidateQueries({ queryKey: hostKey(sessionId) }); - queryClient.invalidateQueries({ queryKey: userKey(sessionId) }); + queryClient.invalidateQueries({ queryKey: hostObjectKey(sessionId) }); + queryClient.invalidateQueries({ queryKey: userObjectKey(sessionId) }); // Invalidate available sessions since this session was deleted queryClient.invalidateQueries({ queryKey: availableSessionsKey() }); // Invalidate all workspace queries since they might reference this session - queryClient.invalidateQueries({ queryKey: ['workspace'] }); + queryClient.invalidateQueries({ queryKey: ['workspace-object'] }); queryClient.invalidateQueries({ queryKey: ['workspace-sessions'] }); queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); }, @@ -212,7 +239,7 @@ export function useLinkSessionsToWorkspace() { mutationFn: async ({ workspaceId, sessionIds }: { workspaceId: string; sessionIds: string[] }) => await linkSessionsToWorkspace(workspaceId, sessionIds), onSuccess: (_result, variables) => { - queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); + queryClient.invalidateQueries({ queryKey: workspaceObjectKey(variables.workspaceId) }); queryClient.invalidateQueries({ queryKey: workspaceSessionsKey(variables.workspaceId) }); // Invalidate workspace stats for the new session configuration queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); @@ -226,7 +253,7 @@ export function useUnlinkSessionFromWorkspace() { mutationFn: async ({ workspaceId, sessionId }: { workspaceId: string; sessionId: string }) => await unlinkSessionFromWorkspace(workspaceId, sessionId), onSuccess: (_result, variables) => { - queryClient.invalidateQueries({ queryKey: workspaceKey(variables.workspaceId) }); + queryClient.invalidateQueries({ queryKey: workspaceObjectKey(variables.workspaceId) }); queryClient.invalidateQueries({ queryKey: workspaceSessionsKey(variables.workspaceId) }); // Invalidate workspace stats for the new session configuration queryClient.invalidateQueries({ queryKey: ['workspace-stats'] }); @@ -252,27 +279,34 @@ export function useSummaryStatus(resourceId: string, isProject = false) { } // For sessions, try to use cached data first to avoid database calls - const cachedHost = queryClient.getQueryData(hostKey(resourceId)); - const cachedUsersForResource = queryClient.getQueryData(userKey(resourceId)); - const cachedUsersIndividual = cachedUsersForResource?.map(user => queryClient.getQueryData(userKey(user.id))); + const cachedHost = queryClient.getQueryData(hostObjectKey(resourceId)); + const cachedUserIds = queryClient.getQueryData(hostUserIdsKey(resourceId)); - if (cachedHost && cachedUsersForResource) { - const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUsersForResource); - const lastUserEdit = cachedUsersForResource.reduce((latest: number, user: UserSession) => { - const lastEditTime = new Date(user.last_edit).getTime(); - return lastEditTime > latest ? lastEditTime : latest; - }, 0); - console.log(`[i] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); - return { - lastEdit: Math.max(lastMessage, lastUserEdit), - lastSummaryUpdate, - resourceId: resourceId - }; + if (cachedHost && cachedUserIds) { + // Get all user sessions from the normalized cache + const cachedUserSessions = cachedUserIds + .map(userId => queryClient.getQueryData(userObjectKey(userId))) + .filter(Boolean) as UserSession[]; + + // TODO: Maybe we could just fetch the missing ones and do this routine...? + if (cachedUserSessions.length === cachedUserIds.length) { + const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUserSessions); + const lastUserEdit = cachedUserSessions.reduce((latest: number, user: UserSession) => { + const lastEditTime = new Date(user.last_edit).getTime(); + return lastEditTime > latest ? lastEditTime : latest; + }, 0); + console.log(`[i] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); + return { + lastEdit: Math.max(lastMessage, lastUserEdit), + lastSummaryUpdate, + resourceId: resourceId + }; + } } - // Fallback to server action if no cached data + // Fallback to server action if not all cached data is available const results = await checkSummaryNeedsUpdating(resourceId, false); - console.log(`[i] Server Summary status: Last ediy: ${results.lastEdit}, Last Summary Update: ${results.lastSummaryUpdate}`); + console.log(`[i] Live Server Summary status: Last edit: ${results.lastEdit}, Last Summary Update: ${results.lastSummaryUpdate}`); return results; }, enabled: !!resourceId, @@ -312,7 +346,7 @@ export function useUpdateSummary() { // Invalidate status to reflect that summary is now up to date queryClient.invalidateQueries({ queryKey: summaryStatusKey(variables.resourceId) }); // Invalidate host session to update last_summary_update timestamp - queryClient.invalidateQueries({ queryKey: hostKey(variables.resourceId) }); + queryClient.invalidateQueries({ queryKey: hostObjectKey(variables.resourceId) }); }, }); } @@ -335,7 +369,7 @@ export function useUpdateHostLastEdit() { return useMutation({ mutationFn: (sessionId: string) => updateHostLastEdit(sessionId), onSuccess: (_result, sessionId) => { - queryClient.invalidateQueries({ queryKey: hostKey(sessionId) }); + queryClient.invalidateQueries({ queryKey: hostObjectKey(sessionId) }); queryClient.invalidateQueries({ queryKey: summaryStatusKey(sessionId) }); }, }); @@ -346,7 +380,7 @@ export function useUpdateWorkspaceLastModified() { return useMutation({ mutationFn: (workspaceId: string) => updateWorkspaceLastModified(workspaceId), onSuccess: (_result, workspaceId) => { - queryClient.invalidateQueries({ queryKey: workspaceKey(workspaceId) }); + queryClient.invalidateQueries({ queryKey: workspaceObjectKey(workspaceId) }); queryClient.invalidateQueries({ queryKey: summaryStatusKey(workspaceId) }); }, }); From 72b14c2689fcea288163145f4b786b7387e2a851 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 20:59:22 +1200 Subject: [PATCH 18/27] More efficient query --- src/stores/SessionStore.ts | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index ad002b90..272210c3 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -50,33 +50,35 @@ export function useHostSession(sessionId: string) { } export function useUserSessions(hostSessionId: string) { - // First, get the user IDs for this host session + const queryClient = useQueryClient(); + + // Fetch user IDs and populate individual user caches in one go const userIdsQuery = useQuery({ queryKey: hostUserIdsKey(hostSessionId), queryFn: async () => { const users = await db.getUsersBySessionId(hostSessionId); + + // Populate individual user entity caches with the data we already have + users.forEach(user => { + queryClient.setQueryData(userObjectKey(user.id), user); + }); + return users.map(user => user.id); }, enabled: !!hostSessionId, staleTime, }); - // Then, get the full user entities - const userEntitiesQueries = useQueries({ - queries: (userIdsQuery.data || []).map(userId => ({ - queryKey: userObjectKey(userId), - queryFn: () => db.getUserSessionById(userId), - enabled: !!userId, - staleTime, - })) - }); + // Get user entities from cache (they should be populated by the query above) + const userSessions = (userIdsQuery.data || []) + .map(userId => queryClient.getQueryData(userObjectKey(userId))) + .filter(Boolean) as UserSession[]; - // Combine the results return { - data: userEntitiesQueries.map(query => query.data).filter(Boolean) as UserSession[], - isLoading: userIdsQuery.isLoading || userEntitiesQueries.some(query => query.isLoading), - isError: userIdsQuery.isError || userEntitiesQueries.some(query => query.isError), - error: userIdsQuery.error || userEntitiesQueries.find(query => query.error)?.error, + data: userSessions, + isLoading: userIdsQuery.isLoading, + isError: userIdsQuery.isError, + error: userIdsQuery.error, }; } From 4a389b5a66ec315d36bdc5a088732f8bf1ee0f24 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 30 Jul 2025 21:09:09 +1200 Subject: [PATCH 19/27] Logging for cache invalidation --- src/lib/db.ts | 16 ++++++++-------- src/stores/SessionStore.ts | 38 +++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/lib/db.ts b/src/lib/db.ts index be596619..229e392f 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1741,12 +1741,12 @@ export async function updateThreadRating( function logCallerLocation(functionName: string, params?: any) { // Only log stack trace if in development - if (process.env.NODE_ENV !== 'production') { - const err = new Error(); - // Only show stack after the first two lines - const splitStack = err.stack?.split('\n'); - const stack = splitStack?.slice(2, 6).filter((line) => !line.includes('node')); - console.log(`[trace] called with params:`, params); - console.debug(`Stack: `, stack); - } + // if (process.env.NODE_ENV !== 'production') { + // const err = new Error(); + // // Only show stack after the first two lines + // const splitStack = err.stack?.split('\n'); + // const stack = splitStack?.slice(2, 6).filter((line) => !line.includes('node')); + // console.log(`[trace] called with params:`, params); + // console.debug(`Stack: `, stack); + // } } \ No newline at end of file diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 272210c3..f2d510c9 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -10,20 +10,20 @@ import { getSession } from '@auth0/nextjs-auth0'; // --- Query Keys --- -const workspaceObjectKey = (id: string) => ['workspace-objects', id]; -const hostObjectKey = (id: string) => ['host-session-objects', id]; -const userObjectKey = (sessionId: string) => ['user-session-objects', sessionId]; +const workspaceObjectKey = (wspaceId: string) => ['workspace-objects', wspaceId]; +const hostObjectKey = (hostId: string) => ['host-session-objects', hostId]; +const userObjectKey = (userSessionId: string) => ['user-session-objects', userSessionId]; // --- Mapping of hostIds -> userIds --- -const hostUserIdsKey = (hostId: string) => ['host-user-ids', hostId]; +const hostToUserIdsKey = (hostId: string) => ['host-user-ids', hostId]; const messagesKey = (threadId: string) => ['messages', threadId]; -const summaryStatusKey = (sessionId: string) => ['summary-status', sessionId]; -const summaryContentKey = (sessionId: string) => ['summary-content', sessionId]; +const summaryStatusKey = (hostSessionId: string) => ['summary-status', hostSessionId]; +const summaryContentKey = (hostSessionId: string) => ['summary-content', hostSessionId]; // Workspace-specific query keys const workspaceSessionsKey = (workspaceId: string) => ['workspace-sessions', workspaceId]; -const sessionsStatsKey = (sessionIds: string[]) => ['workspace-stats', sessionIds.sort()]; +const sessionsStatsKey = (hostSessionIds: string[]) => ['workspace-stats', hostSessionIds.sort()]; const availableSessionsKey = () => ['available-sessions']; const staleTime = 1000 * 60; // 1 minute @@ -39,12 +39,12 @@ export function useWorkspace(workspaceId: string) { }); } -export function useHostSession(sessionId: string) { +export function useHostSession(hostSessionId: string) { return useQuery({ - queryKey: hostObjectKey(sessionId), - queryFn: () => db.getHostSessionById(sessionId), + queryKey: hostObjectKey(hostSessionId), + queryFn: () => db.getHostSessionById(hostSessionId), select: (data) => data ?? [], - enabled: !!sessionId, + enabled: !!hostSessionId, staleTime, }); } @@ -54,7 +54,7 @@ export function useUserSessions(hostSessionId: string) { // Fetch user IDs and populate individual user caches in one go const userIdsQuery = useQuery({ - queryKey: hostUserIdsKey(hostSessionId), + queryKey: hostToUserIdsKey(hostSessionId), queryFn: async () => { const users = await db.getUsersBySessionId(hostSessionId); @@ -63,6 +63,9 @@ export function useUserSessions(hostSessionId: string) { queryClient.setQueryData(userObjectKey(user.id), user); }); + // The return value is what's mapped & cached against the hostSessionId; + // we only want to return/map userSessionIds, not whole objects. + // (We still return all objects later on in the outer return!) return users.map(user => user.id); }, enabled: !!hostSessionId, @@ -190,18 +193,21 @@ export function useUpsertUserSession() { if (!data.id) { return (await db.insertUserSessions(data))[0]; } else { + console.log('[i] Updating existing user session:', data); return (await db.updateUserSession(data.id, data))[0]; } }, onSuccess: (userSessionId, input) => { // Invalidate individual user entity caches + console.log(`Successfully updated user session, now we invalidate the cache for ${userSessionId}`); queryClient.invalidateQueries({ queryKey: userObjectKey(userSessionId) }); // Get host session ID from input data or from local cache to invalidate host-user mapping const hostSessionId = input.session_id || queryClient.getQueryData(userObjectKey(userSessionId))?.session_id; if (hostSessionId) { - queryClient.invalidateQueries({ queryKey: hostUserIdsKey(hostSessionId) }); + console.log(`Invalidating parent hostSession to userId mapping & summaryStatus keys for ${hostSessionId}`); + queryClient.invalidateQueries({ queryKey: hostToUserIdsKey(hostSessionId) }); queryClient.invalidateQueries({ queryKey: summaryStatusKey(hostSessionId) }); } }, @@ -282,22 +288,24 @@ export function useSummaryStatus(resourceId: string, isProject = false) { // For sessions, try to use cached data first to avoid database calls const cachedHost = queryClient.getQueryData(hostObjectKey(resourceId)); - const cachedUserIds = queryClient.getQueryData(hostUserIdsKey(resourceId)); + const cachedUserIds = queryClient.getQueryData(hostToUserIdsKey(resourceId)); if (cachedHost && cachedUserIds) { // Get all user sessions from the normalized cache + console.log(`[1] Checking UserSessions for resourceId: ${resourceId}, sessionIds:`, cachedUserIds); const cachedUserSessions = cachedUserIds .map(userId => queryClient.getQueryData(userObjectKey(userId))) .filter(Boolean) as UserSession[]; // TODO: Maybe we could just fetch the missing ones and do this routine...? if (cachedUserSessions.length === cachedUserIds.length) { + console.log(`[2] Found all required cached user sessions.`) const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUserSessions); const lastUserEdit = cachedUserSessions.reduce((latest: number, user: UserSession) => { const lastEditTime = new Date(user.last_edit).getTime(); return lastEditTime > latest ? lastEditTime : latest; }, 0); - console.log(`[i] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); + console.log(`[3] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); return { lastEdit: Math.max(lastMessage, lastUserEdit), lastSummaryUpdate, From 32ff039da0124aaf7baab9925c7e0d191a604b74 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 31 Jul 2025 09:12:01 +1200 Subject: [PATCH 20/27] Less Logging --- src/app/sessions/[id]/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/sessions/[id]/page.tsx b/src/app/sessions/[id]/page.tsx index 834eabef..8b93df14 100644 --- a/src/app/sessions/[id]/page.tsx +++ b/src/app/sessions/[id]/page.tsx @@ -23,7 +23,6 @@ export default async function SessionResult({ }) { const decryptedId = decryptId(params.id); const queryClient = new QueryClient(); - console.debug(new Error("Debugging Session Page").stack); // Prefetch all session-related data for optimal performance // (This is a server component, data is being prefetched on the server and then dehydrated, passed to the client and then further updates will happen there) From 6dc9eb4a8eb260d9ca7a360528619ca92d85a9ea Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 31 Jul 2025 09:12:01 +1200 Subject: [PATCH 21/27] Fix ThreadUserId --- src/hooks/useChat.ts | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 30990b7e..e57d4315 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -66,6 +66,7 @@ export function useChat(options: UseChatOptions) { const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); const upsertUserSessions = useUpsertUserSession(); const messageInserter = useInsertMessages(); + console.log(`loading chat with usersessionId: ${userSessionId}, data: `, existingUserSession); const placeholder = placeholderText ? placeholderText @@ -189,21 +190,11 @@ export function useChat(options: UseChatOptions) { created_at: new Date(), }); console.log('Inserting new session with initial data: ', data); - return upsertUserSessions.mutateAsync(data) - .then((newUserIds) => { - if (newUserIds && newUserIds.length == 1 && setUserSessionId) { - setUserSessionId(newUserIds[0]); - } - return newUserIds[0]; // Return the userId, just in case setUserSessionId is not fast enough - }) - .catch((error) => { - console.error('[!] error creating user session -> ', error); - setErrorMessage({ - title: 'Failed to create session', - message: 'Oops, that should not have happened. Please try again.', - }); - throw error; // Re-throw the error to be caught by the caller - }); + const userSessionIdFromThreadCreation = await upsertUserSessions.mutateAsync(data) + if (setUserSessionId) { + setUserSessionId(userSessionIdFromThreadCreation); + } + return userSessionIdFromThreadCreation; // Return the userId, just in case setUserSessionId is not fast enough } return undefined; // Return undefined if no sessionId was provided } From 916718ac7c3dc70b1238b25c740f4e4e4c1f867b Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 31 Jul 2025 12:24:32 +1200 Subject: [PATCH 22/27] Fix update checking don't manually try to use cached data! That doesn't work well, introduces unnecessary complexity! --- src/stores/SessionStore.ts | 51 +++----------------------------------- 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index f2d510c9..3a977582 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -272,56 +272,13 @@ export function useUnlinkSessionFromWorkspace() { // --- Summary Operations --- -export function useSummaryStatus(resourceId: string, isProject = false) { - const queryClient = useQueryClient(); - +export function useSummaryStatus(resourceId: string, isProject = false) { return useQuery({ queryKey: summaryStatusKey(resourceId), - queryFn: async () => { - // For projects, we need different caching strategy - if (isProject) { - // For workspaces, check if we have workspace data cached - // const cachedWorkspace = queryClient.getQueryData(workspaceKey(sessionId)); - // No workspace cache, fallback to server action - return checkSummaryNeedsUpdating(resourceId, true); - } - - // For sessions, try to use cached data first to avoid database calls - const cachedHost = queryClient.getQueryData(hostObjectKey(resourceId)); - const cachedUserIds = queryClient.getQueryData(hostToUserIdsKey(resourceId)); - - if (cachedHost && cachedUserIds) { - // Get all user sessions from the normalized cache - console.log(`[1] Checking UserSessions for resourceId: ${resourceId}, sessionIds:`, cachedUserIds); - const cachedUserSessions = cachedUserIds - .map(userId => queryClient.getQueryData(userObjectKey(userId))) - .filter(Boolean) as UserSession[]; - - // TODO: Maybe we could just fetch the missing ones and do this routine...? - if (cachedUserSessions.length === cachedUserIds.length) { - console.log(`[2] Found all required cached user sessions.`) - const { lastMessage, lastSummaryUpdate } = checkSummaryAndMessageTimes(cachedHost, cachedUserSessions); - const lastUserEdit = cachedUserSessions.reduce((latest: number, user: UserSession) => { - const lastEditTime = new Date(user.last_edit).getTime(); - return lastEditTime > latest ? lastEditTime : latest; - }, 0); - console.log(`[3] Cached Summary status for ${resourceId}: Last User Edit: ${lastUserEdit}`); - return { - lastEdit: Math.max(lastMessage, lastUserEdit), - lastSummaryUpdate, - resourceId: resourceId - }; - } - } - - // Fallback to server action if not all cached data is available - const results = await checkSummaryNeedsUpdating(resourceId, false); - console.log(`[i] Live Server Summary status: Last edit: ${results.lastEdit}, Last Summary Update: ${results.lastSummaryUpdate}`); - return results; - }, + queryFn: () => checkSummaryNeedsUpdating(resourceId, isProject), enabled: !!resourceId, - staleTime: 10000, - refetchInterval: 10000, + staleTime, + refetchInterval: 30000, // Check external changes }); } From cded75b326d108e8c70ab8f09607123de5de6429 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 31 Jul 2025 12:24:32 +1200 Subject: [PATCH 23/27] Change to singleton --- src/components/SessionPage/index.tsx | 2 +- .../SessionResult/ExpandableWithExport.tsx | 15 +- .../SessionResult/ResultTabs/index.tsx | 1 + src/components/chat/FullscreenChat.tsx | 3 + src/hooks/useChat.ts | 55 ++-- src/hooks/useSummaryUpdateManager copy.ts | 228 +++++++++++++++++ src/hooks/useSummaryUpdateManager.ts | 216 +++++++--------- src/lib/summary-update-manager.ts | 240 ++++++++++++++++++ src/stores/SessionStore.ts | 8 +- 9 files changed, 616 insertions(+), 152 deletions(-) create mode 100644 src/hooks/useSummaryUpdateManager copy.ts create mode 100644 src/lib/summary-update-manager.ts diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index 72229ae5..204ea252 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -17,7 +17,7 @@ export default function SessionPage({ sessionId }: { sessionId: string }) { } const usersWithChat = userData.filter( - (user) => messageStats[sessionId][user.id].num_messages ?? 0 > 2 + (user) => (messageStats[sessionId]?.[user.id]?.num_messages ?? 0) > 2 ); console.log('message stats: ', messageStats, sessionId) const stats = getUserStats(messageStats, sessionId) diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index 7ca70692..6c8dd0c9 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -10,7 +10,9 @@ import { import ExpandableCard from '../ui/expandable-card'; import { ExportButton } from '../Export/ExportButton'; import { Spinner } from '../icons'; -import { RefreshStatus, useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; +import { useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; +import { RefreshStatus } from '@/lib/summary-update-manager'; + interface CardProps { resourceId: string; @@ -34,6 +36,7 @@ const StatusIndicator = ({ status }: { status: RefreshStatus | undefined }) => { case RefreshStatus.Outdated: return 'bg-red-500'; case RefreshStatus.UpdatePending: + return 'bg-yellow-500'; case RefreshStatus.UpdateStarted: return 'bg-yellow-500 animate-pulse'; case RefreshStatus.Unknown: @@ -60,6 +63,7 @@ export const ExpandableWithExport = ({ loading, className, }: CardProps) => { + console.log(`[HOOK Creation - Expandable] using useSummaryUpdateManager hook`); const summaryManager = useSummaryUpdateManager(resourceId, sessionIds); return ( Up to date

)} {summaryManager.status === RefreshStatus.UpdatePending && ( -

Auto-refreshing soon

+

Refreshing soon

+ )} + {summaryManager.status === RefreshStatus.UpdateStarted && ( +

Fetching updates...

)} {summaryManager.status === RefreshStatus.Outdated && ( -

Summary out of date

+

Out of date

)}
)} diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 2e7cd5ff..326b9670 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -84,6 +84,7 @@ export default function ResultTabs({ const currentUserData = userData; // Declaring this here so that we don't redeclare it whenever the summary tab gets loaded + console.log(`[HOOK Creation - Index] using useSummaryUpdateManager hook`); const summaryUpdateManager = useSummaryUpdateManager(resourceId, sessionIds); const manuallyTriggerSummaryUpdate = async () => { await summaryUpdateManager.startUpdateNow() diff --git a/src/components/chat/FullscreenChat.tsx b/src/components/chat/FullscreenChat.tsx index 94ccad16..0af96a1e 100644 --- a/src/components/chat/FullscreenChat.tsx +++ b/src/components/chat/FullscreenChat.tsx @@ -4,6 +4,7 @@ import { useChat, UseChatOptions } from '../../hooks/useChat'; import { ChatMessages } from './ChatMessages'; import { ChatInput } from './ChatInput'; import ErrorPage from '../Error'; +import { Loader2 } from 'lucide-react'; export function FullscreenChat(props: UseChatOptions) { const chat = useChat(props); @@ -12,6 +13,8 @@ export function FullscreenChat(props: UseChatOptions) { return ; } + console.log(`Showing chat with ${chat.messages.length} messages`) + return (
(threadId); const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); const upsertUserSessions = useUpsertUserSession(); const messageInserter = useInsertMessages(); - console.log(`loading chat with usersessionId: ${userSessionId}, data: `, existingUserSession); const placeholder = placeholderText ? placeholderText @@ -75,9 +76,25 @@ export function useChat(options: UseChatOptions) { const [formData, setFormData] = useState<{ messageText: string }>({ messageText: '', }); - const threadIdRef = useRef(''); const [messages, setMessages] = useState([]); + useEffect(() => { + // Filter out the initial context message if present + console.log(`[i] Restoring messages with initial state isLoading: ${isLoadingMessages}, existingMessages: ${existingMessages.length}, messages: ${messages.length} `); + if (!isLoadingMessages && existingMessages.length > 0 && messages.length === 0) { + const filteredMessages = existingMessages.filter( + msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) + ).map(msg => ({ + role: msg.role as 'user' | 'assistant', + content: msg.content, + is_final: msg.is_final, + })); + + setMessages(filteredMessages); + console.log(`[i] Successfully restored ${filteredMessages.length} messages`); + } + }, [isLoadingMessages, setMessages]); + const addMessage = (newMessage: OpenAIMessage) => { console.log('[Chat] Adding new message:', { content: newMessage.content?.slice(0, 100) + '...', @@ -87,20 +104,39 @@ export function useChat(options: UseChatOptions) { setMessages((prevMessages) => [...prevMessages, newMessage]); }; - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(isLoadingUserSession || isLoadingMessages); const textareaRef = useRef(null); const messagesEndRef = useRef(null); const [isParticipantSuggestionLoading, setIsParticipantSuggestionLoading] = useState(false); + // Effect to update isLoading based on data fetching states + useEffect(() => { + setIsLoading(isLoadingUserSession || isLoadingMessages); + }, [isLoadingUserSession, isLoadingMessages]); + + // Effect to synchronize threadIdRef with the actual threadId from existingUserSession + useEffect(() => { + if (existingUserSession?.thread_id) { + threadIdRef.current = existingUserSession.thread_id; + } + }, [existingUserSession?.thread_id]); + useEffect(() => { if (mainPanelRef?.current && messages.length > 1) { mainPanelRef.current.scrollTop = mainPanelRef.current.scrollHeight; } }, [messages, mainPanelRef]); + // Focus the textarea when the component mounts and show entry message useEffect(() => { - console.log('[i] Chat loading: '); + const textarea = textareaRef.current; + if (entryMessage && messages.length === 0) { + addMessage(entryMessage); + } + if (textarea) { + textarea.focus(); // Automatically focus the textarea + } }, []); const handleInputChange = ( @@ -442,17 +478,6 @@ export function useChat(options: UseChatOptions) { } } - // Focus the textarea when the component mounts and show entry message - useEffect(() => { - const textarea = textareaRef.current; - if (entryMessage && messages.length === 0) { - addMessage(entryMessage); - } - if (textarea) { - textarea.focus(); // Automatically focus the textarea - } - }, []); - return { // State messages, diff --git a/src/hooks/useSummaryUpdateManager copy.ts b/src/hooks/useSummaryUpdateManager copy.ts new file mode 100644 index 00000000..f599472d --- /dev/null +++ b/src/hooks/useSummaryUpdateManager copy.ts @@ -0,0 +1,228 @@ +'use client'; + +import { useEffect, useRef, useCallback, useState } from 'react'; +import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; +import { create } from 'zustand'; + +export enum RefreshStatus { + Unknown, + Outdated, + UpdatePending, + UpdateStarted, + UpToDate, +} + +// Define a global store for managing active updates, +// so that we don't schedule multiple updates from different components +interface UpdateManagerStore { + // Add status for each resourceId + status: Record; + lastRegisteredEdit: Record; + timeouts: Record; + // Add actions to update status + setStatus: (resourceId: string, status: RefreshStatus) => void; + setLastRegisteredEdit: (resourceId: string, lastEdit: number) => void; + // Centralized scheduling method that prevents race conditions + scheduleUpdateIfNeeded: ( + resourceId: string, + summaryStatus: { lastEdit: number; lastSummaryUpdate: number }, + startUpdateCallback: () => Promise + ) => boolean; + clearTimeout: (resourceId: string) => void; +} + +const useUpdateManagerStore = create((set, get) => ({ + status: {}, + lastRegisteredEdit: {}, + timeouts: {}, + + setStatus: (resourceId, status) => + set((state) => { + if (state.status[resourceId] === status) return state; // No change if status is the same; necessary to prevent infinite rerender loops + return { + status: { ...state.status, [resourceId]: status }, + }; + }), + + setLastRegisteredEdit: (resourceId, lastEdit) => + set((state) => ({ + lastRegisteredEdit: { + ...state.lastRegisteredEdit, + [resourceId]: lastEdit, + }, + })), + + clearTimeout: (resourceId) => { + const state = get(); + const timeoutId = state.timeouts[resourceId]; + if (timeoutId) { + clearTimeout(timeoutId); + set((state) => ({ + timeouts: { ...state.timeouts, [resourceId]: null }, + })); + } + }, + + scheduleUpdateIfNeeded: (resourceId, summaryStatus, startUpdateCallback) => { + const state = get(); + + // Check if summary is already up to date + if (summaryStatus.lastSummaryUpdate > summaryStatus.lastEdit) { + set((currentState) => ({ + status: { + ...currentState.status, + [resourceId]: RefreshStatus.UpToDate, + }, + })); + return false; + } + + // Only schedule if no update is currently in progress + if (state.status[resourceId] === RefreshStatus.UpdateStarted) { + return false; + } + + // Check if we've already registered this edit + const lastRegistered = state.lastRegisteredEdit[resourceId] ?? 0; + if (summaryStatus.lastEdit <= lastRegistered) { + return false; + } + + // Update the last registered edit timestamp + set((currentState) => ({ + lastRegisteredEdit: { + ...currentState.lastRegisteredEdit, + [resourceId]: summaryStatus.lastEdit, + }, + })); + + // Clear any existing timeout + const existingTimeout = state.timeouts[resourceId]; + if (existingTimeout) { + clearTimeout(existingTimeout); + } + + // Schedule the update + const delay = 30000; // 30 seconds + const timeoutId = setTimeout(async () => { + + // Double-check that we should still proceed + const currentState = get(); + if (currentState.status[resourceId] === RefreshStatus.UpdateStarted) { + return; + } + + // Set status to started before calling the update + set((state) => ({ + status: { ...state.status, [resourceId]: RefreshStatus.UpdateStarted }, + timeouts: { ...state.timeouts, [resourceId]: null }, + })); + + try { + await startUpdateCallback(); + set((state) => ({ + status: { ...state.status, [resourceId]: RefreshStatus.UpToDate }, + })); + } catch (error) { + console.error( + `[UpdateManagerStore] Update failed for ${resourceId}:`, + error + ); + set((state) => ({ + status: { ...state.status, [resourceId]: RefreshStatus.Outdated }, + })); + } + }, delay); + + // Store the timeout reference + set((currentState) => ({ + status: { + ...currentState.status, + [resourceId]: RefreshStatus.UpdatePending, + }, + timeouts: { ...currentState.timeouts, [resourceId]: timeoutId }, + })); + + return true; + }, +})); + +export function useSummaryUpdateManager( + resourceId: string, + sessionIds?: string[] // Provided for projects +) { + // Use optimized summary status that leverages cached data + // This will be the main source of truth for incoming edits, + // the only other thing that should trigger an update is manually. + const { + data: summaryStatus, + isLoading, + error, + } = useSummaryStatus(resourceId, sessionIds && sessionIds.length > 0); + + const summaryUpdater = useUpdateSummary(); + const { + status, + scheduleUpdateIfNeeded, + clearTimeout: clearStoreTimeout, + } = useUpdateManagerStore(); + + // Clean up timeouts when component unmounts + useEffect(() => { + return () => { + clearStoreTimeout(resourceId); + }; + }, [resourceId, clearStoreTimeout]); + + const startUpdateNow = useCallback(async () => { + // Get current status from store at runtime + const currentStatus = useUpdateManagerStore.getState().status[resourceId]; + + // Prevent starting if already running globally + if ( + summaryUpdater.isPending || + currentStatus === RefreshStatus.UpdateStarted + ) { + return; + } + try { + const result = await summaryUpdater.mutateAsync({ + resourceId, + sessionIds, + }); + return result; + } catch (error) { + console.error( + `[useSummaryUpdateManager] Update failed for ${resourceId}:`, + error + ); + throw error; + } + }, [resourceId, sessionIds, summaryUpdater]); + + // Effect to schedule the summary update using centralized logic + useEffect(() => { + if (isLoading || !summaryStatus) { + return; // Do nothing if loading or no summary status + } + + // Use the centralized scheduler that prevents race conditions + scheduleUpdateIfNeeded(resourceId, summaryStatus, startUpdateNow); + }, [ + summaryStatus, + isLoading, + resourceId, + // Don't include scheduleUpdateIfNeeded - it's not stable and causes infinite loops + startUpdateNow, + ]); + + return { + // State + status: status[resourceId] || RefreshStatus.Unknown, + isLoading, + error: error || summaryUpdater.error, + + // Actions to trigger updates manually + startUpdateNow, + }; +} diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index 21ca24fd..16d67eda 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -1,168 +1,126 @@ 'use client'; -import { useEffect, useRef, useCallback, useState } from 'react'; +import { useEffect, useCallback, useState } from 'react'; import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; -import { create } from 'zustand'; - -export enum RefreshStatus { - Unknown, - Outdated, - UpdatePending, - UpdateStarted, - UpToDate, -} - -// Define a global store for managing active updates, -// so that we don't schedule multiple updates from different components -interface UpdateManagerStore { - // Add status for each resourceId - status: Record; - lastRegisteredEdit: Record; - // Add actions to update status - setStatus: (resourceId: string, status: RefreshStatus) => void; - setLastRegisteredEdit: (resourceId: string, lastEdit: number) => void; -} - -const useUpdateManagerStore = create((set) => ({ - status: {}, - lastRegisteredEdit: {}, - setStatus: (resourceId, status) => - set((state) => { - console.debug(`[UpdateManagerStore] Setting status for ${resourceId} to ${Object.keys(RefreshStatus).filter(k => isNaN(Number(k)))[status]}`); - if (state.status[resourceId] === status) return state; // No change if status is the same; necessary to prevent infinite rerender loops - return ({ - status: { ...state.status, [resourceId]: status }, - }) - }), - setLastRegisteredEdit: (resourceId, lastEdit) => - set((state) => ({ lastRegisteredEdit: { ...state.lastRegisteredEdit, [resourceId]: lastEdit }})) -})); - +// Import the singleton instance and its RefreshStatus enum +import { summaryUpdateManager, RefreshStatus } from '@/lib/summary-update-manager'; +import { useQueryClient } from '@tanstack/react-query'; // Import useQueryClient for potential invalidation + +/** + * A React hook to manage the status and triggering of summary updates for a given resource. + * It integrates with the global SummaryUpdateManager singleton for debounced updates + * and provides status feedback to components. + * + * @param resourceId The unique identifier for the resource (e.g., session ID, workspace ID). + * @param sessionIds Optional array of session IDs if the resource is a project/workspace. + * @returns An object containing the current refresh status, loading state, error, and a function to trigger an update immediately. + */ export function useSummaryUpdateManager( resourceId: string, sessionIds?: string[] // Provided for projects ) { + const queryClient = useQueryClient(); // Get query client for potential invalidation - // Use optimized summary status that leverages cached data - // This will be the main source of truth for incoming edits, - // the only other thing that should trigger an update is manually. + // Use TanStack Query to fetch the summary status (lastEdit, lastSummaryUpdate) + // This is the primary source of truth for detecting if an update is needed. const { data: summaryStatus, - isLoading, - error, + isLoading: isSummaryStatusLoading, + error: summaryStatusError, } = useSummaryStatus(resourceId, sessionIds && sessionIds.length > 0); + // TanStack Query mutation hook for performing the actual summary update const summaryUpdater = useUpdateSummary(); - const timeoutIdRef = useRef(null); - const { status, setStatus, lastRegisteredEdit, setLastRegisteredEdit } = useUpdateManagerStore(); + // Local React state to reflect the RefreshStatus from the singleton. + // This ensures the component re-renders when the status changes. + const [currentStatus, setCurrentStatus] = useState( + summaryUpdateManager.getStatus(resourceId) + ); + + /** + * Effect to subscribe to status changes from the SummaryUpdateManager singleton. + * The callback updates the local React state, causing component re-renders. + */ useEffect(() => { + const unsubscribe = summaryUpdateManager.subscribe(resourceId, (status) => { + setCurrentStatus(status); + }); + + // Cleanup function: unsubscribe when the component unmounts or resourceId changes. + // Optionally, you might call summaryUpdateManager.cleanup(resourceId) here + // if this hook instance is the definitive lifecycle owner for this resourceId. + // However, if multiple components might use the same resourceId, + // cleanup should be managed at a higher level (e.g., when the resource is truly deleted). return () => { - if (timeoutIdRef.current) { - clearTimeout(timeoutIdRef.current); - } + unsubscribe(); }; - }, [resourceId]); + }, [resourceId]); // Re-subscribe if resourceId changes - const startUpdateNow = useCallback(async () => { - // Prevent starting if already running globally or if this instance thinks it's started - console.debug(`[useSummaryUpdateManager] startUpdateNow called for ${resourceId} with status: ${status[resourceId]}`); - if ( - summaryUpdater.isPending || - status[resourceId] === RefreshStatus.UpdateStarted - ) { - console.debug(`[useSummaryUpdateManager] Update already running for ${resourceId}.`); - return; - } - - setStatus(resourceId, RefreshStatus.UpdateStarted); - - console.debug(`[useSummaryUpdateManager] Update triggered for ${resourceId} from `, new Error().stack); + /** + * Callback function that performs the actual summary update via TanStack Query mutation. + * This function is passed to the singleton, so it can execute the update when needed. + */ + const startUpdateMutation = useCallback(async () => { try { const result = await summaryUpdater.mutateAsync({ resourceId, sessionIds, }); - - console.debug(`[useSummaryUpdateManager] Update completed for ${resourceId}`); + // After a successful update, invalidate the summary status query + // to ensure the latest lastSummaryUpdate timestamp is fetched. + console.log(`Update successful, invalidating summary stats`) + await queryClient.invalidateQueries({ queryKey: ['summary-status', resourceId] }); return result; } catch (error) { - console.error( - `[useSummaryUpdateManager] Update failed for ${resourceId}:`, - error - ); - throw error; - } finally { - setStatus(resourceId, RefreshStatus.UpToDate); + console.error(`[useSummaryUpdateManager] Mutation failed for ${resourceId}:`, error); + throw error; // Re-throw to allow singleton to handle status } - }, [ - resourceId, - sessionIds, - summaryUpdater, - setStatus, - status[resourceId], - ]); + }, [resourceId, sessionIds, summaryUpdater, queryClient]); - // Effect to schedule the summary update + /** + * Effect to trigger the singleton's scheduleUpdate method. + * This runs whenever `summaryStatus` data changes (indicating a potential edit) + * or when the hook's dependencies change. + */ useEffect(() => { - if (isLoading || !summaryStatus) { - console.debug(`[useSummaryUpdateManager] No summary status or loading for ${resourceId}`); - setStatus(resourceId, RefreshStatus.Unknown); - return; // Do nothing if loading or no summary status - } - - // Only schedule if no update is currently in progress globally - if (status[resourceId] === RefreshStatus.UpdateStarted) { - console.debug(`[useSummaryUpdateManager] Update already started for ${resourceId}`); - return; // Don't schedule if an update is already running - } - - if (summaryStatus.lastSummaryUpdate > summaryStatus.lastEdit) { - setStatus(resourceId, RefreshStatus.UpToDate); + // Guard: Do not schedule if summary status data is still loading or not available. + if (isSummaryStatusLoading || !summaryStatus) { return; } - - const delay = 30000; // 30 seconds - console.debug( - `[useSummaryUpdateManager] lastRegisteredEdit for ${resourceId}: ${lastRegisteredEdit[resourceId] ?? 0}` - ) - if (summaryStatus.lastEdit > (lastRegisteredEdit[resourceId] ?? 0)) { - console.debug( - `[useSummaryUpdateManager] Registered some edit. Schedule summary update for ${resourceId}. -Last edit: ${summaryStatus.lastEdit} -Last summary update: ${summaryStatus.lastSummaryUpdate}` - ); - - setLastRegisteredEdit(resourceId, summaryStatus.lastEdit); - - // Reset existing timers - if (timeoutIdRef.current) { - console.debug(`[useSummaryUpdateManager] Replacing existing timeout for ${resourceId}`); - clearTimeout(timeoutIdRef.current); - } - // Schedule the update - timeoutIdRef.current = setTimeout(() => { - startUpdateNow(); - }, delay); - setStatus(resourceId, RefreshStatus.UpdatePending); - } + // Pass the current summary status data and the mutation callback to the singleton. + // The singleton will handle the debouncing and actual update scheduling. + summaryUpdateManager.scheduleUpdate(resourceId, summaryStatus, startUpdateMutation); }, [ - startUpdateNow, - summaryStatus, - isLoading, + summaryStatus, // Trigger when lastEdit or lastSummaryUpdate changes + isSummaryStatusLoading, resourceId, - setStatus, - setLastRegisteredEdit, + startUpdateMutation, // Ensure the latest callback is used ]); - return { - // State - status: status[resourceId] || RefreshStatus.Unknown, - isLoading, - error: error || summaryUpdater.error, + /** + * Function to manually trigger a summary update immediately, bypassing the debounce. + * This is typically called by a UI action (e.g., a "Refresh" button). + */ + const startUpdateNow = useCallback(async () => { + try { + // Delegate the immediate update logic to the singleton. + await summaryUpdateManager.startUpdateNow(resourceId, startUpdateMutation); + } catch (err) { + // Error is already logged by the singleton, re-throw if the component needs to catch it. + throw err; + } + }, [resourceId, startUpdateMutation]); - // Actions to trigger updates manually + return { + // The current refresh status, synchronized from the singleton. + status: currentStatus, + // Loading state for the initial summary status query. + isLoading: isSummaryStatusLoading, + // Combined error from summary status query or the update mutation. + error: summaryStatusError || summaryUpdater.error, + // Function to manually trigger an update. startUpdateNow, }; } \ No newline at end of file diff --git a/src/lib/summary-update-manager.ts b/src/lib/summary-update-manager.ts new file mode 100644 index 00000000..a294ed19 --- /dev/null +++ b/src/lib/summary-update-manager.ts @@ -0,0 +1,240 @@ +/** + * @file This file defines the SummaryUpdateManager singleton class, + * responsible for centralizing and debouncing summary update logic. + * It manages the status, timeouts, and last edit timestamps for each resource, + * and provides a subscription mechanism for React components. + */ + +export enum RefreshStatus { + Unknown = 'Unknown', + Outdated = 'Outdated', + UpdatePending = 'UpdatePending', + UpdateStarted = 'UpdateStarted', + UpToDate = 'UpToDate', +} + +interface SummaryStatusData { + lastEdit: number; + lastSummaryUpdate: number; +} + +type StatusUpdateCallback = (status: RefreshStatus) => void; + +/** + * SummaryUpdateManager is a singleton class that handles the debounced + * scheduling and execution of summary updates for various resources. + * It ensures that only one update is pending or in progress for a given resource + * and provides a mechanism for UI components to react to status changes. + */ +class SummaryUpdateManagerClass { + private static instance: SummaryUpdateManagerClass; + + // Stores the current RefreshStatus for each resourceId + private statuses: Map = new Map(); + // Stores the NodeJS.Timeout ID for pending debounced updates + private scheduledUpdates: Map = new Map(); + // Stores the timestamp of the last edit that was registered for a potential update + private lastRegisteredEdits: Map = new Map(); + // Stores sets of callback functions subscribed to status changes for each resourceId + private subscribers: Map> = new Map(); + + /** + * Private constructor to enforce the singleton pattern. + */ + private constructor() { } + + /** + * Returns the single instance of the SummaryUpdateManager. + * @returns The singleton instance of SummaryUpdateManagerClass. + */ + public static getInstance(): SummaryUpdateManagerClass { + if (!SummaryUpdateManagerClass.instance) { + SummaryUpdateManagerClass.instance = new SummaryUpdateManagerClass(); + } + return SummaryUpdateManagerClass.instance; + } + + /** + * Internal method to update the status of a resource and notify all its subscribers. + * @param resourceId The ID of the resource. + * @param status The new RefreshStatus for the resource. + */ + private setStatus(resourceId: string, status: RefreshStatus): void { + // Only update and notify if the status has actually changed + if (this.statuses.get(resourceId) === status) { + return; + } + this.statuses.set(resourceId, status); + this.notifySubscribers(resourceId, status); + } + + /** + * Notifies all registered callback functions for a specific resourceId with its current status. + * @param resourceId The ID of the resource. + * @param status The current RefreshStatus of the resource. + */ + private notifySubscribers(resourceId: string, status: RefreshStatus): void { + const callbacks = this.subscribers.get(resourceId); + if (callbacks) { + // Use a new Set to avoid issues if subscribers unsubscribe during iteration + new Set(callbacks).forEach(callback => { + try { + callback(status); + } catch (e) { + console.error(`[SummaryUpdateManager] Error notifying subscriber for ${resourceId}:`, e); + } + }); + } + } + + /** + * Subscribes a callback function to status updates for a given resourceId. + * @param resourceId The ID of the resource. + * @param callback The function to be called when the resource's status changes. + * @returns A function to unsubscribe the callback. + */ + public subscribe(resourceId: string, callback: StatusUpdateCallback): () => void { + if (!this.subscribers.has(resourceId)) { + this.subscribers.set(resourceId, new Set()); + } + this.subscribers.get(resourceId)?.add(callback); + + // Immediately provide the current status to the new subscriber + callback(this.getStatus(resourceId)); + + // Return an unsubscribe function for cleanup + return () => { + const callbacks = this.subscribers.get(resourceId); + if (callbacks) { + callbacks.delete(callback); + // Clean up the Set if no more subscribers for this resource + if (callbacks.size === 0) { + this.subscribers.delete(resourceId); + } + } + }; + } + + /** + * Retrieves the current RefreshStatus for a given resourceId. + * @param resourceId The ID of the resource. + * @returns The current RefreshStatus, or Unknown if not found. + */ + public getStatus(resourceId: string): RefreshStatus { + return this.statuses.get(resourceId) || RefreshStatus.Unknown; + } + + /** + * Clears any pending debounce timeout for a specific resourceId. + * @param resourceId The ID of the resource. + */ + private clearScheduledUpdate(resourceId: string): void { + const timeoutId = this.scheduledUpdates.get(resourceId); + if (timeoutId) { + clearTimeout(timeoutId); + this.scheduledUpdates.delete(resourceId); + } + } + + /** + * Schedules a summary update for a resource with a 30-second debounce. + * This method should be called whenever a potential change is detected (e.g., lastEdit timestamp changes). + * It will only trigger an actual update after 30 seconds of inactivity for that resource. + * @param resourceId The ID of the resource (e.g., session or workspace). + * @param summaryStatus The current summary status data (lastEdit, lastSummaryUpdate). + * @param startUpdateCallback A function that, when called, initiates the actual summary update (e.g., a TanStack Query mutation). + */ + public scheduleUpdate( + resourceId: string, + summaryStatus: SummaryStatusData, + startUpdateCallback: () => Promise + ): void { + // 1. Guard: If summary is already up to date, clear any pending updates and set status. + if (summaryStatus.lastSummaryUpdate >= summaryStatus.lastEdit) { + this.setStatus(resourceId, RefreshStatus.UpToDate); + this.clearScheduledUpdate(resourceId); // Ensure no pending updates + return; + } + + // 2. Guard: If an update is currently in progress, do not schedule another. + if (this.getStatus(resourceId) === RefreshStatus.UpdateStarted) { + return; + } + + // 3. Guard: Check if we've already registered this specific edit. + // This prevents re-scheduling if the lastEdit hasn't genuinely advanced since last check. + const lastRegistered = this.lastRegisteredEdits.get(resourceId) ?? 0; + if (summaryStatus.lastEdit <= lastRegistered) { + return; + } + + // Update the last registered edit timestamp to the current lastEdit + this.lastRegisteredEdits.set(resourceId, summaryStatus.lastEdit); + + // Clear any existing debounce timeout for this resource to reset the timer + this.clearScheduledUpdate(resourceId); + + // Set status to pending as an update is now scheduled + this.setStatus(resourceId, RefreshStatus.UpdatePending); + + const debounceDelayMs = 30000; // 30 seconds debounce delay + + // Schedule the actual update after the debounce delay + const scheduleId = setTimeout(async () => { + this.startUpdateNow(resourceId, startUpdateCallback) + }, debounceDelayMs); + + // Store the new timeout ID + this.scheduledUpdates.set(resourceId, scheduleId); + } + + /** + * Manually triggers a summary update for a resource, bypassing the debounce delay. + * @param resourceId The ID of the resource. + * @param startUpdateCallback A function that initiates the actual summary update. + * @returns A Promise that resolves when the update is complete, or rejects if it fails. + */ + public async startUpdateNow(resourceId: string, startUpdateCallback: () => Promise): Promise { + // Clear any pending debounced update for this resource + this.clearScheduledUpdate(resourceId); + + // Guard: Prevent starting if an update is already running + if (this.getStatus(resourceId) === RefreshStatus.UpdateStarted) { + console.warn(`[SummaryUpdateManager] Manual update for ${resourceId} aborted: already running.`); + return; + } + + // Set status to 'UpdateStarted' + this.setStatus(resourceId, RefreshStatus.UpdateStarted); + + try { + await startUpdateCallback(); // Execute the actual update + this.setStatus(resourceId, RefreshStatus.UpToDate); + // Mark as updated to prevent immediate re-trigger from debouncer + this.lastRegisteredEdits.set(resourceId, Date.now()); + } catch (error) { + console.error( + `[SummaryUpdateManager] Manual update failed for ${resourceId}:`, + error + ); + this.setStatus(resourceId, RefreshStatus.Outdated); + throw error; // Re-throw to allow the calling component to handle the error + } + } + + /** + * Cleans up all state associated with a specific resourceId. + * This should be called when a resource is no longer active or mounted in the application. + * @param resourceId The ID of the resource to clean up. + */ + public cleanup(resourceId: string): void { + this.clearScheduledUpdate(resourceId); // Clear any pending timeouts + this.statuses.delete(resourceId); + this.lastRegisteredEdits.delete(resourceId); + this.subscribers.delete(resourceId); // Remove all subscribers for this resource + console.log(`[SummaryUpdateManager] Cleaned up state for ${resourceId}`); + } +} + +// Export the singleton instance for global access +export const summaryUpdateManager = SummaryUpdateManagerClass.getInstance(); \ No newline at end of file diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 3a977582..7f84d7e7 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -275,10 +275,12 @@ export function useUnlinkSessionFromWorkspace() { export function useSummaryStatus(resourceId: string, isProject = false) { return useQuery({ queryKey: summaryStatusKey(resourceId), - queryFn: () => checkSummaryNeedsUpdating(resourceId, isProject), + queryFn: () => { + console.log(`Fetching summary status for ${resourceId}`); + return checkSummaryNeedsUpdating(resourceId, isProject) + }, enabled: !!resourceId, - staleTime, - refetchInterval: 30000, // Check external changes + refetchInterval: 30000, }); } From b63a82832d375ca8b06e76bb06a4d705f4f955c0 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 1 Aug 2025 11:44:25 +1200 Subject: [PATCH 24/27] Clean Up --- src/app/actions/process-invitations.ts | 7 +- src/app/providers.tsx | 4 - src/app/sessions/[id]/page.tsx | 2 +- src/app/workspace/[w_id]/page.tsx | 7 +- src/components/SessionPage/index.tsx | 1 - .../SessionResult/ExpandableWithExport.tsx | 4 +- .../ResultTabs/hooks/useCustomResponses.ts | 2 - .../SessionResult/ResultTabs/index.tsx | 10 - .../SessionResult/SessionResultChat.tsx | 5 - .../SessionResult/SessionResultSummary.tsx | 8 +- src/components/chat/ChatInterface.tsx | 13 - src/components/chat/FullscreenChat.tsx | 3 - src/hooks/useChat.ts | 210 +++++++--------- src/hooks/useSummary.ts | 13 - src/hooks/useSummaryUpdateManager copy.ts | 228 ------------------ src/stores/SessionStore.ts | 27 ++- 16 files changed, 109 insertions(+), 435 deletions(-) delete mode 100644 src/hooks/useSummary.ts delete mode 100644 src/hooks/useSummaryUpdateManager copy.ts diff --git a/src/app/actions/process-invitations.ts b/src/app/actions/process-invitations.ts index 826f06f2..fcca2365 100644 --- a/src/app/actions/process-invitations.ts +++ b/src/app/actions/process-invitations.ts @@ -21,7 +21,6 @@ export async function processUserInvitations(): Promise<{ // Get the authenticated user const session = await getSession(); if (!session || !session.user) { - console.log(`User session not available`); return { success: false, @@ -42,7 +41,7 @@ export async function processUserInvitations(): Promise<{ const userId = session.user.sub; if (!userEmail || !userId) { - console.log( + console.warn( `User email or ID (sub) not available: `, JSON.stringify(session.user, null, 2) ); @@ -59,9 +58,6 @@ export async function processUserInvitations(): Promise<{ // Find pending invitations for this email const invitations = await getInvitationsByEmail(userEmail); - console.log( - `Found ${invitations.length} pending invitations for ${userEmail}` - ); let processed = 0; // Process each invitation @@ -81,7 +77,6 @@ export async function processUserInvitations(): Promise<{ } } - console.log(`Processed ${processed} invitations for user ${userEmail}`); return { success: true, processed, diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 4c919764..2543c762 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -50,10 +50,6 @@ function InvitationProcessor() { const { toast } = useToast(); useEffect(() => { - // Check for invitations when user logs in - console.log( - `Checking invitations: IsLoading:${isLoading}; hasUser: ${!!user}`, - ); if (!isLoading && user) { const processInvitations = async () => { try { diff --git a/src/app/sessions/[id]/page.tsx b/src/app/sessions/[id]/page.tsx index 8b93df14..c31ad0f4 100644 --- a/src/app/sessions/[id]/page.tsx +++ b/src/app/sessions/[id]/page.tsx @@ -28,7 +28,7 @@ export default async function SessionResult({ // (This is a server component, data is being prefetched on the server and then dehydrated, passed to the client and then further updates will happen there) // TanStack is taking care of the hydration magic. try { - console.log(`Prefetching data...`); + console.log(`Prefetching session data...`); await Promise.allSettled([ // Prefetch host session data diff --git a/src/app/workspace/[w_id]/page.tsx b/src/app/workspace/[w_id]/page.tsx index 988e8ace..9820bd58 100644 --- a/src/app/workspace/[w_id]/page.tsx +++ b/src/app/workspace/[w_id]/page.tsx @@ -27,8 +27,7 @@ export default async function Workspace({ params: { w_id: string }; }) { const queryClient = new QueryClient(); - console.log(`Call stack in Workspace page.tsx:`, new Error("Debugging Workspace Page").stack); - + try { // First prefetch workspace data await queryClient.prefetchQuery({ @@ -41,7 +40,7 @@ export default async function Workspace({ // Only prefetch individual session data if workspace exists and has sessions if (data.exists && data.sessionIds.length > 0) { - console.log(`Prefetching data for ${data.sessionIds.length} sessions...`); + console.log(`Prefetching workspace data for ${data.sessionIds.length} sessions...`); await Promise.allSettled([ // Prefetch all individual session data for cache reuse @@ -88,7 +87,7 @@ export default async function Workspace({ }) ]); - console.log('Prefetching completed successfully'); + console.log('Prefetching workspaces completed successfully'); } return ( diff --git a/src/components/SessionPage/index.tsx b/src/components/SessionPage/index.tsx index 204ea252..e5b48bef 100644 --- a/src/components/SessionPage/index.tsx +++ b/src/components/SessionPage/index.tsx @@ -19,7 +19,6 @@ export default function SessionPage({ sessionId }: { sessionId: string }) { const usersWithChat = userData.filter( (user) => (messageStats[sessionId]?.[user.id]?.num_messages ?? 0) > 2 ); - console.log('message stats: ', messageStats, sessionId) const stats = getUserStats(messageStats, sessionId) const status = !hostData.active || hostData.final_report_sent diff --git a/src/components/SessionResult/ExpandableWithExport.tsx b/src/components/SessionResult/ExpandableWithExport.tsx index 6c8dd0c9..3c5f8ed7 100644 --- a/src/components/SessionResult/ExpandableWithExport.tsx +++ b/src/components/SessionResult/ExpandableWithExport.tsx @@ -58,12 +58,10 @@ export const ExpandableWithExport = ({ isExpanded, onExpandedChange, showRefreshButton, - onRefresh, isUpdating, loading, className, }: CardProps) => { - console.log(`[HOOK Creation - Expandable] using useSummaryUpdateManager hook`); const summaryManager = useSummaryUpdateManager(resourceId, sessionIds); return (
{ setIsLoading(true); - console.log(`Getting custom blocks for ressource ${id} - ${responseType}`); db.getCustomResponsesByResourceIdAndType(id, responseType) .then((response) => { - console.log(`Got ${response.length} responses`); setResponses(response); }) .catch((err) => { diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 326b9670..5388ccfc 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -83,17 +83,8 @@ export default function ResultTabs({ const currentUserData = userData; - // Declaring this here so that we don't redeclare it whenever the summary tab gets loaded - console.log(`[HOOK Creation - Index] using useSummaryUpdateManager hook`); - const summaryUpdateManager = useSummaryUpdateManager(resourceId, sessionIds); - const manuallyTriggerSummaryUpdate = async () => { - await summaryUpdateManager.startUpdateNow() - }; - - // Define available tabs and their visibility conditions in one place const availableTabs = useMemo(() => { - console.log('Computing available tabs!'); return [ { id: 'SUMMARY', @@ -112,7 +103,6 @@ export default function ResultTabs({ (hasMinimumRole('editor') || visibilityConfig.showSummary) ?? true } showSessionRecap={visibilityConfig.showSessionRecap ?? true} - onSummaryUpdateTrigger={manuallyTriggerSummaryUpdate} /> ), }, diff --git a/src/components/SessionResult/SessionResultChat.tsx b/src/components/SessionResult/SessionResultChat.tsx index ba6fdc8e..7a8e7e81 100644 --- a/src/components/SessionResult/SessionResultChat.tsx +++ b/src/components/SessionResult/SessionResultChat.tsx @@ -42,11 +42,6 @@ Here are a few examples of what you can ask me: {userData && userData.length > 0 && ( user.include_in_summary), - }} sessionIds={ sessionIds && sessionIds.length > 0 ? sessionIds diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index 267d3be1..73a92cc1 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { ExpandableWithExport } from './ExpandableWithExport'; import { Card, CardContent } from '../ui/card'; import { usePermissions } from '@/lib/permissions'; -import { useSummary } from '@/hooks/useSummary'; +import { useSummary } from '@/stores/SessionStore'; interface SessionResultSummaryProps { hostData: HostSession[]; @@ -13,7 +13,6 @@ interface SessionResultSummaryProps { draft: boolean; showSummary?: boolean; showSessionRecap?: boolean; - onSummaryUpdateTrigger?: () => void; } export default function SessionResultSummary({ @@ -23,9 +22,7 @@ export default function SessionResultSummary({ draft = false, showSummary = true, showSessionRecap = true, - onSummaryUpdateTrigger }: SessionResultSummaryProps) { - const [isUpdating, setIsUpdating] = useState(false); const [isExpandedPrompt, setIsExpandedPrompt] = useState(false); const [isExpandedSummary, setIsExpandedSummary] = useState(true); @@ -64,8 +61,7 @@ export default function SessionResultSummary({ isExpanded={isExpandedSummary} onExpandedChange={setIsExpandedSummary} showRefreshButton={hasMinimumRole('editor')} - onRefresh={onSummaryUpdateTrigger} - loading={summaryLoading || isUpdating} + loading={summaryLoading} /> ) : showDraftProjectCard ? ( diff --git a/src/components/chat/ChatInterface.tsx b/src/components/chat/ChatInterface.tsx index d4635ea1..087fd953 100644 --- a/src/components/chat/ChatInterface.tsx +++ b/src/components/chat/ChatInterface.tsx @@ -53,15 +53,6 @@ export const ChatInterface = ({ setThreadId(id); }; - const handleFinish = () => { - setIsSessionFinished(true); - onFinish(); - // Show rating modal after 2 seconds - setTimeout(() => { - setShowRating(true); - }, 2000); - }; - useEffect(() => { console.log('[ChatInterface] Message or state changed:', { messageContent: message?.content?.slice(0, 100) + '...', @@ -72,12 +63,8 @@ export const ChatInterface = ({ }); if (message?.is_final && threadId) { - console.log( - '[ChatInterface] Final message detected, showing rating modal in 2s', - ); // Show rating modal after 2 seconds when final message is received setTimeout(() => { - console.log('[ChatInterface] Showing rating modal now'); setShowRating(true); }, 2000); } diff --git a/src/components/chat/FullscreenChat.tsx b/src/components/chat/FullscreenChat.tsx index 0af96a1e..94ccad16 100644 --- a/src/components/chat/FullscreenChat.tsx +++ b/src/components/chat/FullscreenChat.tsx @@ -4,7 +4,6 @@ import { useChat, UseChatOptions } from '../../hooks/useChat'; import { ChatMessages } from './ChatMessages'; import { ChatInput } from './ChatInput'; import ErrorPage from '../Error'; -import { Loader2 } from 'lucide-react'; export function FullscreenChat(props: UseChatOptions) { const chat = useChat(props); @@ -13,8 +12,6 @@ export function FullscreenChat(props: UseChatOptions) { return ; } - console.log(`Showing chat with ${chat.messages.length} messages`) - return (
void; userSessionId?: string; entryMessage?: OpenAIMessage; - context?: OpenAIMessageWithContext; placeholderText?: string; userContext?: Record; isAskAi?: boolean; @@ -34,11 +33,11 @@ export interface UseChatOptions { export function useChat(options: UseChatOptions) { const { sessionIds, - setUserSessionId, - userSessionId, - entryMessage, - context, - placeholderText, + setUserSessionId, // setUserSessionId is set when createThread is called (here); so when useChat is first called + userSessionId, // userSessionId will always be empty, only on later iterations it will be available; + // OR it might be available if it's restored from local storage (set in the setUserSessionid setter!) + entryMessage, + placeholderText = 'Type your message here...', userContext, isAskAi = false, crossPollination = false, @@ -60,51 +59,29 @@ export function useChat(options: UseChatOptions) { const [errorToastMessage, setErrorToastMessage] = useState(''); const { user } = useUser(); - // Get existing user session data if userSessionId is provided + // Get existing user session data if userSessionId is provided. + // Because messages rely on threadId, this might go through multiple re-renders, + // so we need to make sure to load some of the data properly in useEffects. + // TODO: unify userSessionId & threadId; they're basically both the same just a different column in the db. + // (but wait for AI to be good enough to replace it and unify!) + + // If we don't have a userSessionId then much of this wouldn't be necessary, but unfortunately we can't make it conditional, that would break react stuff. const { data: existingUserSession, isLoading: isLoadingUserSession } = useUserSession(userSessionId || ''); const threadId = existingUserSession?.thread_id || ''; - console.log(`[i] Using threadId: ${threadId} for userSessionId: ${userSessionId}`); const threadIdRef = useRef(threadId); + const createThreadInProgressRef = useRef(false); const { data: existingMessages = [], isLoading: isLoadingMessages } = useMessages(threadId); const upsertUserSessions = useUpsertUserSession(); const messageInserter = useInsertMessages(); - - const placeholder = placeholderText - ? placeholderText - : 'Type your message here...'; - - const [formData, setFormData] = useState<{ messageText: string }>({ - messageText: '', - }); const [messages, setMessages] = useState([]); - - useEffect(() => { - // Filter out the initial context message if present - console.log(`[i] Restoring messages with initial state isLoading: ${isLoadingMessages}, existingMessages: ${existingMessages.length}, messages: ${messages.length} `); - if (!isLoadingMessages && existingMessages.length > 0 && messages.length === 0) { - const filteredMessages = existingMessages.filter( - msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) - ).map(msg => ({ - role: msg.role as 'user' | 'assistant', - content: msg.content, - is_final: msg.is_final, - })); - - setMessages(filteredMessages); - console.log(`[i] Successfully restored ${filteredMessages.length} messages`); - } - }, [isLoadingMessages, setMessages]); - const addMessage = (newMessage: OpenAIMessage) => { - console.log('[Chat] Adding new message:', { - content: newMessage.content?.slice(0, 100) + '...', - is_final: newMessage.is_final, - role: newMessage.role, - }); setMessages((prevMessages) => [...prevMessages, newMessage]); }; - const [isLoading, setIsLoading] = useState(isLoadingUserSession || isLoadingMessages); + const [formData, setFormData] = useState<{ messageText: string }>({ + messageText: '', + }); + const textareaRef = useRef(null); const messagesEndRef = useRef(null); const [isParticipantSuggestionLoading, setIsParticipantSuggestionLoading] = @@ -122,22 +99,64 @@ export function useChat(options: UseChatOptions) { } }, [existingUserSession?.thread_id]); + // Focus the textarea when the component mounts + useEffect(() => { + const textarea = textareaRef.current; + if (textarea) { + textarea.focus(); // Automatically focus the textarea + } + }, []); + + // Scroll to the bottom of the chat when new messages are added useEffect(() => { if (mainPanelRef?.current && messages.length > 1) { mainPanelRef.current.scrollTop = mainPanelRef.current.scrollHeight; } }, [messages, mainPanelRef]); - // Focus the textarea when the component mounts and show entry message + // Setup initial messages: either restore existing messages, + // or set the entry message (if available, usually for AskAI) useEffect(() => { - const textarea = textareaRef.current; - if (entryMessage && messages.length === 0) { - addMessage(entryMessage); - } - if (textarea) { - textarea.focus(); // Automatically focus the textarea + // Filter out the initial context message if present + console.log(`Has entry message? ${!!entryMessage}: ${entryMessage}`); + if (!isLoadingMessages && messages.length === 0) { + if (existingMessages.length > 0) { + const filteredMessages = existingMessages.filter( + msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) + ).map(msg => ({ + role: msg.role as 'user' | 'assistant', + content: msg.content, + is_final: msg.is_final, + })); + + setMessages(filteredMessages); + } else if (entryMessage) { + addMessage(entryMessage); + } + } + }, [isLoadingMessages, setMessages]); + + // Hook to create a new thread (and with that also a userSessionId; threads & userSessionId are tightly coupled and should be unified to one in a future iteration) + useEffect(() => { + if (!userSessionId && !createThreadInProgressRef.current) { + if (!sessionIds || sessionIds.length != 1) { + // We have this as a separate check mainly for compiler warnings, it would be flagged otherwise. + throw new Error('Cannot create a thread without a session ID or with multiple session IDs.'); + } + if (!userContext) { + throw new Error('User context screen was skipt in some way') + } + const userName = getUserNameFromContext(userContext); + createThread( + sessionIds[0], + user ? user : 'id', + userName, + userContext, + ).then((newUserSessionId) => { + handleSubmit(undefined, true, newUserSessionId); // first AI message + }); } - }, []); + }, [userSessionId]); // purposely only passing in userSessionId just to make clear that this is the only relevant var here and for thread creation! const handleInputChange = ( e: React.ChangeEvent, @@ -145,8 +164,6 @@ export function useChat(options: UseChatOptions) { setFormData({ ...formData, [e.target.name]: e.target.value }); }; - const createThreadInProgressRef = useRef(false); - const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !isLoading) { if (e.metaKey) { @@ -189,6 +206,14 @@ export function useChat(options: UseChatOptions) { if (isTesting) { return undefined; } + + createThreadInProgressRef.current = true; + + // TODO: I think we can actually replace separate threadIds with the ID that's created when the db entry is inserted, + // or vice versa. They're always both created here, both are equally unique, and there's always a one-one mapping. + // However, it will require a bit of updates and checking where everything is used, + // and probably the removal of the threadId column in the db, which is just a bit work. + // (But wait until AI is a bit better than it is right now, and then let AI just to all of this menial work ;-)) const threadId = crypto.randomUUID(); threadIdRef.current = threadId; if (onThreadIdReceived) { @@ -200,9 +225,8 @@ export function useChat(options: UseChatOptions) { ? user + '_' + crypto.randomUUID() : user.sub || 'unknown'; - if (sessionId) { const data = { - session_id: sessionId, + session_id: sessionId!, user_id: userId, user_name: userName, thread_id: threadId, @@ -210,6 +234,7 @@ export function useChat(options: UseChatOptions) { start_time: new Date(), last_edit: new Date(), }; + //insert user formdata const contextString = userContext ? Object.entries(userContext) @@ -225,67 +250,14 @@ export function useChat(options: UseChatOptions) { content: `User shared the following context:\n${contextString}`, created_at: new Date(), }); - console.log('Inserting new session with initial data: ', data); + console.debug('Inserting new session with initial data: ', data); const userSessionIdFromThreadCreation = await upsertUserSessions.mutateAsync(data) if (setUserSessionId) { setUserSessionId(userSessionIdFromThreadCreation); } return userSessionIdFromThreadCreation; // Return the userId, just in case setUserSessionId is not fast enough - } - return undefined; // Return undefined if no sessionId was provided } - useEffect(() => { - if ( - userContext && - !threadIdRef.current && - !createThreadInProgressRef.current && - !isAskAi // AskAI doesn't actually use the OpenAI thread we're creating here; it uses whatever is specified in the llama API. - // Also, AskAI doesn't use the userContext, so this parameter combination should never be triggered (unless maybe on page load?), but just for clarity we put it here anyway. - ) { - const userName = getUserNameFromContext(userContext); - - createThreadInProgressRef.current = true; - - // Check if we have an existing userSessionId to restore - if (userSessionId && existingUserSession && !isLoadingUserSession) { - // Restore existing thread - threadIdRef.current = existingUserSession.thread_id; - if (onThreadIdReceived) { - onThreadIdReceived(existingUserSession.thread_id); - } - - // Filter out the initial context message if present - if (existingMessages) { - const filteredMessages = existingMessages.filter( - msg => !(msg.role === 'user' && msg.content?.startsWith('User shared the following context:')) - ).map(msg => ({ - role: msg.role as 'user' | 'assistant', - content: msg.content, - is_final: msg.is_final, - })); - - setMessages(filteredMessages); - console.log(`[i] Successfully restored ${existingMessages.length} messages for thread ${existingUserSession.thread_id}`); - } - } else if (!userSessionId || (!existingUserSession && !isLoadingUserSession) || (!existingMessages && isLoadingMessages)) { - // No existing session or session not found, create new thread - if (!sessionIds || sessionIds.length != 1) { - throw new Error('Cannot create a thread without a session ID or with multiple session IDs.'); - } - createThread( - sessionIds[0], - user ? user : 'id', - userName, - userContext, - ).then((threadSessionId) => { - handleSubmit(undefined, true, threadSessionId); - }); - } - } - }, [userContext, userSessionId, existingUserSession, isLoadingUserSession]); - - const handleSubmit = async ( e?: React.FormEvent, isAutomatic?: boolean, @@ -295,7 +267,7 @@ export function useChat(options: UseChatOptions) { e.preventDefault(); } - if (isLoading) return; + if (isLoading) return; // We're not ready yet. There might be an existing setIsLoading(true); if (isTesting) { addMessage({ @@ -321,30 +293,24 @@ export function useChat(options: UseChatOptions) { textareaRef.current?.focus(); } - const now = new Date(); if (!isAskAi) { await waitForThreadCreation(threadIdRef, setErrorMessage); } - + const currentUserSessionId = userSessionId || userSessionIdFromThread; - + if (currentUserSessionId && !isAutomatic && !isAskAi) { - console.log( - `[i] Inserting chat message for user session ${currentUserSessionId}`, - ); - // Need to do await here, because we process this chat on the server - // and we need to wait here until it actually is written to the database; - // i.e. optimistic client-side updates won't be enough! + // Need to do await here, because we process this chat on the server and we need to wait + // until it actually is written to the database, otherwise the response will be empty. await messageInserter.mutateAsync({ thread_id: threadIdRef.current, role: 'user', content: messageText, - created_at: now, + created_at: new Date(), }); } if (isAskAi) { - console.log(`[i] Asking AI for response`); const response = await fetch('/api/llama', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -371,7 +337,6 @@ export function useChat(options: UseChatOptions) { messageText, sessionId: sessionIds?.[0] || '', }; - console.log(`[i] Generating answer for message: ${messageText}`); llama .handleGenerateAnswer( messageData, @@ -387,7 +352,6 @@ export function useChat(options: UseChatOptions) { const now = new Date(); addMessage(answer); - console.log(`Got reply; updating messages and upserting user session: ${currentUserSessionId}`); if (currentUserSessionId) { Promise.all([ messageInserter.mutateAsync({ @@ -474,7 +438,7 @@ export function useChat(options: UseChatOptions) { } await new Promise((resolve) => setTimeout(resolve, 500)); waitedCycles++; - console.log(`Waiting ${waitedCycles} cycles for thread to be created...`); + console.warn(`Waited ${waitedCycles} cycles for thread to be created...`); } } @@ -486,7 +450,7 @@ export function useChat(options: UseChatOptions) { isParticipantSuggestionLoading, errorMessage, errorToastMessage, - placeholder, + placeholder: placeholderText, // Refs textareaRef, diff --git a/src/hooks/useSummary.ts b/src/hooks/useSummary.ts deleted file mode 100644 index d0db1177..00000000 --- a/src/hooks/useSummary.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { fetchSummary } from '@/lib/summaryActions'; - -export function useSummary(resourceId: string, initialSummary?: string, isProject = false) { - return useQuery({ - queryKey: ['summary-content', resourceId], - queryFn: () => fetchSummary(resourceId, isProject), - enabled: !!resourceId, - initialData: initialSummary, - staleTime: 1000 * 60, // 1 minute - refetchOnWindowFocus: false, - }); -} diff --git a/src/hooks/useSummaryUpdateManager copy.ts b/src/hooks/useSummaryUpdateManager copy.ts deleted file mode 100644 index f599472d..00000000 --- a/src/hooks/useSummaryUpdateManager copy.ts +++ /dev/null @@ -1,228 +0,0 @@ -'use client'; - -import { useEffect, useRef, useCallback, useState } from 'react'; -import { useSummaryStatus, useUpdateSummary } from '@/stores/SessionStore'; -import { create } from 'zustand'; - -export enum RefreshStatus { - Unknown, - Outdated, - UpdatePending, - UpdateStarted, - UpToDate, -} - -// Define a global store for managing active updates, -// so that we don't schedule multiple updates from different components -interface UpdateManagerStore { - // Add status for each resourceId - status: Record; - lastRegisteredEdit: Record; - timeouts: Record; - // Add actions to update status - setStatus: (resourceId: string, status: RefreshStatus) => void; - setLastRegisteredEdit: (resourceId: string, lastEdit: number) => void; - // Centralized scheduling method that prevents race conditions - scheduleUpdateIfNeeded: ( - resourceId: string, - summaryStatus: { lastEdit: number; lastSummaryUpdate: number }, - startUpdateCallback: () => Promise - ) => boolean; - clearTimeout: (resourceId: string) => void; -} - -const useUpdateManagerStore = create((set, get) => ({ - status: {}, - lastRegisteredEdit: {}, - timeouts: {}, - - setStatus: (resourceId, status) => - set((state) => { - if (state.status[resourceId] === status) return state; // No change if status is the same; necessary to prevent infinite rerender loops - return { - status: { ...state.status, [resourceId]: status }, - }; - }), - - setLastRegisteredEdit: (resourceId, lastEdit) => - set((state) => ({ - lastRegisteredEdit: { - ...state.lastRegisteredEdit, - [resourceId]: lastEdit, - }, - })), - - clearTimeout: (resourceId) => { - const state = get(); - const timeoutId = state.timeouts[resourceId]; - if (timeoutId) { - clearTimeout(timeoutId); - set((state) => ({ - timeouts: { ...state.timeouts, [resourceId]: null }, - })); - } - }, - - scheduleUpdateIfNeeded: (resourceId, summaryStatus, startUpdateCallback) => { - const state = get(); - - // Check if summary is already up to date - if (summaryStatus.lastSummaryUpdate > summaryStatus.lastEdit) { - set((currentState) => ({ - status: { - ...currentState.status, - [resourceId]: RefreshStatus.UpToDate, - }, - })); - return false; - } - - // Only schedule if no update is currently in progress - if (state.status[resourceId] === RefreshStatus.UpdateStarted) { - return false; - } - - // Check if we've already registered this edit - const lastRegistered = state.lastRegisteredEdit[resourceId] ?? 0; - if (summaryStatus.lastEdit <= lastRegistered) { - return false; - } - - // Update the last registered edit timestamp - set((currentState) => ({ - lastRegisteredEdit: { - ...currentState.lastRegisteredEdit, - [resourceId]: summaryStatus.lastEdit, - }, - })); - - // Clear any existing timeout - const existingTimeout = state.timeouts[resourceId]; - if (existingTimeout) { - clearTimeout(existingTimeout); - } - - // Schedule the update - const delay = 30000; // 30 seconds - const timeoutId = setTimeout(async () => { - - // Double-check that we should still proceed - const currentState = get(); - if (currentState.status[resourceId] === RefreshStatus.UpdateStarted) { - return; - } - - // Set status to started before calling the update - set((state) => ({ - status: { ...state.status, [resourceId]: RefreshStatus.UpdateStarted }, - timeouts: { ...state.timeouts, [resourceId]: null }, - })); - - try { - await startUpdateCallback(); - set((state) => ({ - status: { ...state.status, [resourceId]: RefreshStatus.UpToDate }, - })); - } catch (error) { - console.error( - `[UpdateManagerStore] Update failed for ${resourceId}:`, - error - ); - set((state) => ({ - status: { ...state.status, [resourceId]: RefreshStatus.Outdated }, - })); - } - }, delay); - - // Store the timeout reference - set((currentState) => ({ - status: { - ...currentState.status, - [resourceId]: RefreshStatus.UpdatePending, - }, - timeouts: { ...currentState.timeouts, [resourceId]: timeoutId }, - })); - - return true; - }, -})); - -export function useSummaryUpdateManager( - resourceId: string, - sessionIds?: string[] // Provided for projects -) { - // Use optimized summary status that leverages cached data - // This will be the main source of truth for incoming edits, - // the only other thing that should trigger an update is manually. - const { - data: summaryStatus, - isLoading, - error, - } = useSummaryStatus(resourceId, sessionIds && sessionIds.length > 0); - - const summaryUpdater = useUpdateSummary(); - const { - status, - scheduleUpdateIfNeeded, - clearTimeout: clearStoreTimeout, - } = useUpdateManagerStore(); - - // Clean up timeouts when component unmounts - useEffect(() => { - return () => { - clearStoreTimeout(resourceId); - }; - }, [resourceId, clearStoreTimeout]); - - const startUpdateNow = useCallback(async () => { - // Get current status from store at runtime - const currentStatus = useUpdateManagerStore.getState().status[resourceId]; - - // Prevent starting if already running globally - if ( - summaryUpdater.isPending || - currentStatus === RefreshStatus.UpdateStarted - ) { - return; - } - try { - const result = await summaryUpdater.mutateAsync({ - resourceId, - sessionIds, - }); - return result; - } catch (error) { - console.error( - `[useSummaryUpdateManager] Update failed for ${resourceId}:`, - error - ); - throw error; - } - }, [resourceId, sessionIds, summaryUpdater]); - - // Effect to schedule the summary update using centralized logic - useEffect(() => { - if (isLoading || !summaryStatus) { - return; // Do nothing if loading or no summary status - } - - // Use the centralized scheduler that prevents race conditions - scheduleUpdateIfNeeded(resourceId, summaryStatus, startUpdateNow); - }, [ - summaryStatus, - isLoading, - resourceId, - // Don't include scheduleUpdateIfNeeded - it's not stable and causes infinite loops - startUpdateNow, - ]); - - return { - // State - status: status[resourceId] || RefreshStatus.Unknown, - isLoading, - error: error || summaryUpdater.error, - - // Actions to trigger updates manually - startUpdateNow, - }; -} diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 7f84d7e7..7f741108 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,15 +1,16 @@ -import { useQuery, useMutation, useQueryClient, useQueries } from '@tanstack/react-query'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import * as db from '@/lib/db'; import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace, HostSession, UserSession, User } from '@/lib/schema'; import { fetchWorkspaceData } from '@/lib/workspaceData'; import { linkSessionsToWorkspace, unlinkSessionFromWorkspace } from '@/lib/workspaceActions'; import { checkSummaryNeedsUpdating, fetchSummary, updateUserLastEdit, updateHostLastEdit, updateWorkspaceLastModified } from '@/lib/summaryActions'; -import { checkSummaryAndMessageTimes } from '@/lib/clientUtils'; import { createSummary, createMultiSessionSummary } from '@/lib/serverUtils'; import { getSession } from '@auth0/nextjs-auth0'; // --- Query Keys --- +// Note: resourceId can be wspaceId OR hostId; everything else can only be what it says. +// hostId == hostSessionId, they're used interchangeably const workspaceObjectKey = (wspaceId: string) => ['workspace-objects', wspaceId]; const hostObjectKey = (hostId: string) => ['host-session-objects', hostId]; const userObjectKey = (userSessionId: string) => ['user-session-objects', userSessionId]; @@ -18,12 +19,12 @@ const userObjectKey = (userSessionId: string) => ['user-session-objects', userSe const hostToUserIdsKey = (hostId: string) => ['host-user-ids', hostId]; const messagesKey = (threadId: string) => ['messages', threadId]; -const summaryStatusKey = (hostSessionId: string) => ['summary-status', hostSessionId]; -const summaryContentKey = (hostSessionId: string) => ['summary-content', hostSessionId]; +const summaryStatusKey = (resourceId: string) => ['summary-status', resourceId]; +const summaryContentKey = (resourceId: string) => ['summary-content', resourceId]; // Workspace-specific query keys const workspaceSessionsKey = (workspaceId: string) => ['workspace-sessions', workspaceId]; -const sessionsStatsKey = (hostSessionIds: string[]) => ['workspace-stats', hostSessionIds.sort()]; +const sessionsStatsKey = (hostIds: string[]) => ['workspace-stats', hostIds.sort()]; const availableSessionsKey = () => ['available-sessions']; const staleTime = 1000 * 60; // 1 minute @@ -193,20 +194,17 @@ export function useUpsertUserSession() { if (!data.id) { return (await db.insertUserSessions(data))[0]; } else { - console.log('[i] Updating existing user session:', data); return (await db.updateUserSession(data.id, data))[0]; } }, onSuccess: (userSessionId, input) => { // Invalidate individual user entity caches - console.log(`Successfully updated user session, now we invalidate the cache for ${userSessionId}`); queryClient.invalidateQueries({ queryKey: userObjectKey(userSessionId) }); // Get host session ID from input data or from local cache to invalidate host-user mapping const hostSessionId = input.session_id || queryClient.getQueryData(userObjectKey(userSessionId))?.session_id; if (hostSessionId) { - console.log(`Invalidating parent hostSession to userId mapping & summaryStatus keys for ${hostSessionId}`); queryClient.invalidateQueries({ queryKey: hostToUserIdsKey(hostSessionId) }); queryClient.invalidateQueries({ queryKey: summaryStatusKey(hostSessionId) }); } @@ -276,20 +274,23 @@ export function useSummaryStatus(resourceId: string, isProject = false) { return useQuery({ queryKey: summaryStatusKey(resourceId), queryFn: () => { - console.log(`Fetching summary status for ${resourceId}`); + console.debug(`Fetching summary status for ${resourceId}`); return checkSummaryNeedsUpdating(resourceId, isProject) }, enabled: !!resourceId, refetchInterval: 30000, + refetchOnWindowFocus: true }); } -export function useSummaryContent(sessionId: string) { +export function useSummary(resourceId: string, initialSummary?: string, isProject = false) { return useQuery({ - queryKey: summaryContentKey(sessionId), - queryFn: () => fetchSummary(sessionId), - enabled: !!sessionId, + queryKey: summaryContentKey(resourceId), + queryFn: () => fetchSummary(resourceId, isProject), + enabled: !!resourceId, + initialData: initialSummary, staleTime, + refetchOnWindowFocus: false, }); } From b0aef70e7efc2d5725baa16867c068b3e6b3061d Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 4 Aug 2025 13:42:56 +1200 Subject: [PATCH 25/27] Fix Workspace tanstack Fix Tanstack for Workspace --- src/app/workspace/[w_id]/actions.ts | 129 ++++++++++++++++++ src/app/workspace/[w_id]/page.tsx | 96 +++++++------ .../SessionResult/ParticipantSessionRow.tsx | 2 +- .../SessionResult/ResultTabs/index.tsx | 1 - .../SessionResult/SessionResultSummary.tsx | 7 +- .../SessionResult/SessionSummaryCard.tsx | 19 ++- .../SessionResult/TranscriptPopup.tsx | 7 +- .../workspace/SessionInsightsGrid.tsx | 9 +- src/hooks/useChat.ts | 9 +- src/lib/workspaceData.ts | 2 +- src/stores/SessionStore.ts | 42 +----- 11 files changed, 206 insertions(+), 117 deletions(-) diff --git a/src/app/workspace/[w_id]/actions.ts b/src/app/workspace/[w_id]/actions.ts index e3560c78..a9f31eb9 100644 --- a/src/app/workspace/[w_id]/actions.ts +++ b/src/app/workspace/[w_id]/actions.ts @@ -2,6 +2,10 @@ import { put } from '@vercel/blob'; import * as db from '@/lib/db'; +import { getSession } from '@auth0/nextjs-auth0'; +import { NewWorkspace, HostSession, UserSession } from '@/lib/schema'; +import { hasAccessToResource } from '@/lib/serverUtils'; +import { ExtendedWorkspaceData } from '@/lib/types'; export async function deleteWorkspace(id: string) { try { @@ -43,4 +47,129 @@ export async function uploadBanner( }); return blob.url; +} + +export async function fetchWorkspaceDataAction(workspaceId: string): Promise { + console.log("Fetching project data for workspace ", workspaceId); + + // Helper function to get available sessions for linking + async function getAllAvailableSessionIds() { + const session = await getSession(); + const userId = session?.user?.sub; + const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); + const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); + let availableSessions: ExtendedWorkspaceData["availableSessions"] = []; + if (availableSessionsIds.length > 0) { + availableSessions = await db.getHostSessionsForIds(availableSessionsIds, [ + 'id', + 'topic', + 'start_time' + ]); + } + return availableSessions; + } + + // First, check whether this workspace exists at all. If not, add a 'draft' mode with the current user as the owner: + const workspaceExists = await db.hasWorkspace(workspaceId); + if (!workspaceExists) { + const draftWorkspace: NewWorkspace = { + id: workspaceId, + title: "New Project", + status: 'draft', + gradientFrom: '#6B21A8', + gradientTo: '#9333EA', + useGradient: true, + }; + + await db.createWorkspace(draftWorkspace); + await db.setPermission(workspaceId, 'owner', 'WORKSPACE'); + const availableSessions = await getAllAvailableSessionIds(); + + return { + exists: false, + workspace: draftWorkspace, + hostSessions: [], + userData: [], + sessionIds: [], + availableSessions + }; + } + + // Check if user has access to this workspace + const hasAccess = await hasAccessToResource(workspaceId); + if (!hasAccess) { + throw new Error('Access denied: You do not have permission to view this Project'); + } + + let workspaceData = await db.getWorkspaceById(workspaceId); + if (!workspaceData) { + throw new Error('Project not found'); + } + if (workspaceData.status == 'deleted') { + // The workspace was marked for deletion, but hasn't actually been deleted. Let's do that now. + await db.deleteWorkspace(workspaceId); + console.log("Deleting ", workspaceId); + throw new Error('Project has been removed.'); + } + + const availableSessions = await getAllAvailableSessionIds(); + + console.log(`Found Project ${workspaceId}!`); + // Fetch all necessary data for existing workspace + const sessionIds = await db.getWorkspaceSessionIds(workspaceId); + + let hostSessions: HostSession[]; + let allUserData: UserSession[][]; + + // Fetch host sessions and user sessions + [hostSessions, allUserData] = await Promise.all([ + Promise.all(sessionIds.map((id) => db.getHostSessionById(id))), + Promise.all(sessionIds.map((id) => db.getUsersBySessionId(id))), + ]); + + // Get stats for all sessions + const stats = await db.getNumUsersAndMessages( + hostSessions.map((session) => session.id), + ); + + // Flatten user data (no filtering by message count - let client handle this) + const userData = allUserData.flat(); + + return { + exists: workspaceData.status !== 'draft', + workspace: workspaceData, + hostSessions, + userData, + sessionIds, + availableSessions, + }; +} + +export async function fetchAvailableSessionsAction() { + const session = await getSession(); + const userId = session?.user?.sub; + const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); + const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); + + if (availableSessionsIds.length > 0) { + return await db.getHostSessionsForIds(availableSessionsIds, [ + 'id', 'topic', 'start_time' + ]); + } + return []; +} + +export async function fetchAvailableWorkspacesAction() { + const session = await getSession(); + const userId = session?.user?.sub; + + if (!userId) { + return []; + } + + const resources = await db.getResourcesForUser(userId, 'WORKSPACE'); + if (!resources.length) return []; + + const workspaceIds = resources.map(resource => resource.resource_id); + return await db.getWorkspacesForIds(workspaceIds, ['id', 'title']); } \ No newline at end of file diff --git a/src/app/workspace/[w_id]/page.tsx b/src/app/workspace/[w_id]/page.tsx index 9820bd58..d37188bf 100644 --- a/src/app/workspace/[w_id]/page.tsx +++ b/src/app/workspace/[w_id]/page.tsx @@ -1,25 +1,20 @@ -import { Metadata } from 'next'; -import { getGeneratedMetadata } from 'app/api/metadata'; -import WorkspaceContent from './WorkspaceContent'; -import ErrorPage from '@/components/Error'; -import { fetchWorkspaceData } from '@/lib/workspaceData'; -import { ExtendedWorkspaceData } from '@/lib/types'; -import { QueryClient, dehydrate, HydrationBoundary } from '@tanstack/react-query'; -import * as db from '@/lib/db'; -import { getSession } from '@auth0/nextjs-auth0'; +import ErrorPage from "@/components/Error"; +import { QueryClient, HydrationBoundary, dehydrate } from "@tanstack/react-query"; +import * as db from "@/lib/db"; +import { fetchWorkspaceDataAction, fetchAvailableSessionsAction } from "./actions"; +import WorkspaceContent from "./WorkspaceContent";// Increase the maximum execution time for this function on vercel +import { getGeneratedMetadata } from "app/api/metadata"; +import { Metadata } from "next/types"; +export const maxDuration = 300; // in seconds +export const revalidate = 0; // Disable caching for this page -// Increase the maximum execution time for this function on vercel -// export const maxDuration = 300; // in seconds -// export const revalidate = 0; // Disable caching for this page - - -// export async function generateMetadata({ -// params, -// }: { -// params: { w_id: string }; -// }): Promise { -// return getGeneratedMetadata(`/workspace/${params.w_id}`); -// } +export async function generateMetadata({ + params, +}: { + params: { w_id: string }; +}): Promise { + return getGeneratedMetadata(`/workspace/${params.w_id}`); +} export default async function Workspace({ params, @@ -29,28 +24,43 @@ export default async function Workspace({ const queryClient = new QueryClient(); try { - // First prefetch workspace data - await queryClient.prefetchQuery({ - queryKey: ['workspace', params.w_id], - queryFn: () => fetchWorkspaceData(params.w_id, queryClient) + // Use the server action for consistency + const extendedWorkspaceData = await fetchWorkspaceDataAction(params.w_id); + + // Set individual query caches for optimal performance + const { sessionIds, hostSessions, userData } = extendedWorkspaceData; + + // Create allUserData structure for cache population + const allUserData = sessionIds.map(sessionId => + userData.filter(user => user.session_id === sessionId) + ); + + sessionIds.forEach((id, index) => { + queryClient.setQueryData(['host', id], hostSessions[index]); + queryClient.setQueryData(['users', id], allUserData[index]); }); - // Get the cached workspace data to access session IDs for further prefetching - const data: ExtendedWorkspaceData = queryClient.getQueryData(['workspace', params.w_id])!; + // Set workspace-specific caches + queryClient.setQueryData(['workspace-sessions', params.w_id], sessionIds); + queryClient.setQueryData(['available-sessions'], extendedWorkspaceData.availableSessions); + queryClient.setQueryData(['workspace', params.w_id], extendedWorkspaceData); - // Only prefetch individual session data if workspace exists and has sessions - if (data.exists && data.sessionIds.length > 0) { - console.log(`Prefetching workspace data for ${data.sessionIds.length} sessions...`); + // Continue with additional prefetch queries for performance optimization + if (sessionIds.length > 0) { + console.log(`Prefetching workspace data for ${sessionIds.length} sessions...`); + + const stats = await db.getNumUsersAndMessages(sessionIds); + queryClient.setQueryData(['workspace-stats', sessionIds.sort()], stats); await Promise.allSettled([ - // Prefetch all individual session data for cache reuse - ...data.sessionIds.map(id => + // These will use cached data if available + ...sessionIds.map(id => queryClient.prefetchQuery({ queryKey: ['host', id], queryFn: () => db.getHostSessionById(id) }) ), - ...data.sessionIds.map(id => + ...sessionIds.map(id => queryClient.prefetchQuery({ queryKey: ['users', id], queryFn: () => db.getUsersBySessionId(id) @@ -64,26 +74,14 @@ export default async function Workspace({ }), queryClient.prefetchQuery({ - queryKey: ['workspace-stats', data.sessionIds.sort()], - queryFn: () => db.getNumUsersAndMessages(data.sessionIds) + queryKey: ['workspace-stats', sessionIds.sort()], + queryFn: () => db.getNumUsersAndMessages(sessionIds) }), // Prefetch available sessions queryClient.prefetchQuery({ queryKey: ['available-sessions'], - queryFn: async () => { - const session = await getSession(); - const userId = session?.user?.sub; - const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); - const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); - - if (availableSessionsIds.length > 0) { - return await db.getHostSessionsForIds(availableSessionsIds, [ - 'id', 'topic', 'start_time' - ]); - } - return []; - } + queryFn: () => fetchAvailableSessionsAction() }) ]); @@ -94,7 +92,7 @@ export default async function Workspace({
diff --git a/src/components/SessionResult/ParticipantSessionRow.tsx b/src/components/SessionResult/ParticipantSessionRow.tsx index 9d0d41a1..5681ebef 100644 --- a/src/components/SessionResult/ParticipantSessionRow.tsx +++ b/src/components/SessionResult/ParticipantSessionRow.tsx @@ -105,7 +105,7 @@ export default function ParicipantSessionRow({ } diff --git a/src/components/SessionResult/ResultTabs/index.tsx b/src/components/SessionResult/ResultTabs/index.tsx index 5388ccfc..d47de6a3 100644 --- a/src/components/SessionResult/ResultTabs/index.tsx +++ b/src/components/SessionResult/ResultTabs/index.tsx @@ -26,7 +26,6 @@ import { import { SimScoreTab } from './SimScoreTab'; import SessionFilesTable from '../SessionFilesTable'; import { useHostSession, useUserSessions, useWorkspace } from '@/stores/SessionStore'; -import { useSummaryUpdateManager } from '@/hooks/useSummaryUpdateManager'; export interface ResultTabsProps { resourceId: string; diff --git a/src/components/SessionResult/SessionResultSummary.tsx b/src/components/SessionResult/SessionResultSummary.tsx index 73a92cc1..b3000daf 100644 --- a/src/components/SessionResult/SessionResultSummary.tsx +++ b/src/components/SessionResult/SessionResultSummary.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { ExpandableWithExport } from './ExpandableWithExport'; import { Card, CardContent } from '../ui/card'; import { usePermissions } from '@/lib/permissions'; -import { useSummary } from '@/stores/SessionStore'; +import { useSummary, useWorkspace } from '@/stores/SessionStore'; interface SessionResultSummaryProps { hostData: HostSession[]; @@ -28,9 +28,8 @@ export default function SessionResultSummary({ const resourceId: string = isProject ? projectId! : hostData[0].id; const { hasMinimumRole } = usePermissions(resourceId); - - // Use SWR to fetch summary content with initial data as fallback - const initialSummary = isProject ? '' : hostData[0]?.summary || ''; + const { data: workspaceData } = useWorkspace(resourceId); + const initialSummary = isProject ? workspaceData?.workspace.summary : hostData[0]?.summary || ''; const { data: summary, isLoading: summaryLoading } = useSummary(resourceId, initialSummary, isProject); // Check which content will be shown diff --git a/src/components/SessionResult/SessionSummaryCard.tsx b/src/components/SessionResult/SessionSummaryCard.tsx index e1826593..19866459 100644 --- a/src/components/SessionResult/SessionSummaryCard.tsx +++ b/src/components/SessionResult/SessionSummaryCard.tsx @@ -6,33 +6,32 @@ import { intlFormatDistance } from 'date-fns'; import { encryptId } from '@/lib/encryptionUtils'; import { Button } from '@/components/ui/button'; import { X } from 'lucide-react'; +import { useSessionsStats } from '@/stores/SessionStore'; export default function SessionSummaryCard({ hostData, userData, - workspace_id, - id, + sessionId, onRemove, }: { hostData: HostSession; userData: UserSession[]; - workspace_id: string; - id: string; + sessionId: string; onRemove?: (sessionId: string) => void; }) { + const { data: messageStats, isLoading: isLoadingStats } = useSessionsStats([sessionId]) + const totalUsers = userData.filter(user => messageStats?.[sessionId][user.id].num_messages ?? 0 > 2).length; + const handleRemove = (e: React.MouseEvent) => { e.preventDefault(); // Prevent the Link from navigating e.stopPropagation(); // Stop event propagation if (onRemove) { if (confirm(`Remove "${hostData.topic}" from this workspace?`)) { - onRemove(id); + onRemove(sessionId); } } }; - // Not 100% what we use elsewhere (where we actually check for how many users have sent more than 2 messages); - // but maybe good enough for now. - const totalUsers = userData.filter(user => user.include_in_summary).length; const status = !hostData.active || hostData.final_report_sent @@ -54,7 +53,7 @@ export default function SessionSummaryCard({ )} - + @@ -71,7 +70,7 @@ export default function SessionSummaryCard({
Participants
-
{userData.length}
+
{isLoadingStats ? '...' : totalUsers}
Started
diff --git a/src/components/SessionResult/TranscriptPopup.tsx b/src/components/SessionResult/TranscriptPopup.tsx index 11e3e286..fcd7727e 100644 --- a/src/components/SessionResult/TranscriptPopup.tsx +++ b/src/components/SessionResult/TranscriptPopup.tsx @@ -39,13 +39,14 @@ const EMOJI_RATINGS = [ export default function TranscriptPopup({ threadId, handleCloseClick, - tableData, + userName, }: { threadId: string; handleCloseClick: () => void; - tableData: ParticipantsTableData; + userName: string; }) { const [rating, setRating] = useState(null); + console.log('Getting messages for threadId ', threadId) const { data: messages = [], isLoading } = useMessages(threadId); useEffect(() => { @@ -64,7 +65,7 @@ export default function TranscriptPopup({

- {tableData.userName}'s Conversation + {userName}'s Conversation

{rating && (
diff --git a/src/components/workspace/SessionInsightsGrid.tsx b/src/components/workspace/SessionInsightsGrid.tsx index 2c53e0eb..05080b54 100644 --- a/src/components/workspace/SessionInsightsGrid.tsx +++ b/src/components/workspace/SessionInsightsGrid.tsx @@ -20,6 +20,7 @@ import { useWorkspace, useLinkSessionsToWorkspace, useUnlinkSessionFromWorkspace, + useSessionsStats, } from '@/stores/SessionStore'; interface SessionInsightsGridProps { @@ -106,21 +107,15 @@ export default function SessionInsightsGrid({ const sessionUserData = localUserData.filter( (user) => user.session_id === hostData.id ); - - // Calculate the number of participants - const participantCount = sessionUserData.length; - return ( ); diff --git a/src/hooks/useChat.ts b/src/hooks/useChat.ts index 36855743..990dd712 100644 --- a/src/hooks/useChat.ts +++ b/src/hooks/useChat.ts @@ -118,7 +118,7 @@ export function useChat(options: UseChatOptions) { // or set the entry message (if available, usually for AskAI) useEffect(() => { // Filter out the initial context message if present - console.log(`Has entry message? ${!!entryMessage}: ${entryMessage}`); + console.log(`Has entry message? ${!!entryMessage}:`, entryMessage); if (!isLoadingMessages && messages.length === 0) { if (existingMessages.length > 0) { const filteredMessages = existingMessages.filter( @@ -138,13 +138,10 @@ export function useChat(options: UseChatOptions) { // Hook to create a new thread (and with that also a userSessionId; threads & userSessionId are tightly coupled and should be unified to one in a future iteration) useEffect(() => { - if (!userSessionId && !createThreadInProgressRef.current) { + if (userContext && !userSessionId && !createThreadInProgressRef.current) { if (!sessionIds || sessionIds.length != 1) { // We have this as a separate check mainly for compiler warnings, it would be flagged otherwise. - throw new Error('Cannot create a thread without a session ID or with multiple session IDs.'); - } - if (!userContext) { - throw new Error('User context screen was skipt in some way') + throw new Error(`Cannot create a thread without a session ID or with multiple session IDs: ${sessionIds}`); } const userName = getUserNameFromContext(userContext); createThread( diff --git a/src/lib/workspaceData.ts b/src/lib/workspaceData.ts index a4b2bae9..012d2995 100644 --- a/src/lib/workspaceData.ts +++ b/src/lib/workspaceData.ts @@ -28,7 +28,7 @@ export async function fetchWorkspaceData( queryClient?: QueryClient ): Promise { try { - console.log("Fetching optimized project data for workspace ", workspaceId); + console.log("Fetching project data for workspace ", workspaceId); // First, check whether this workspace exists at all. If not, add a 'draft' mode with the current user as the owner: const workspaceExists = await db.hasWorkspace(workspaceId); diff --git a/src/stores/SessionStore.ts b/src/stores/SessionStore.ts index 7f741108..4314ea96 100644 --- a/src/stores/SessionStore.ts +++ b/src/stores/SessionStore.ts @@ -1,11 +1,10 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import * as db from '@/lib/db'; import { NewHostSession, NewMessage, NewUserSession, NewWorkspace, Workspace, HostSession, UserSession, User } from '@/lib/schema'; -import { fetchWorkspaceData } from '@/lib/workspaceData'; import { linkSessionsToWorkspace, unlinkSessionFromWorkspace } from '@/lib/workspaceActions'; +import { fetchWorkspaceDataAction, fetchAvailableSessionsAction, fetchAvailableWorkspacesAction } from 'app/workspace/[w_id]/actions'; import { checkSummaryNeedsUpdating, fetchSummary, updateUserLastEdit, updateHostLastEdit, updateWorkspaceLastModified } from '@/lib/summaryActions'; import { createSummary, createMultiSessionSummary } from '@/lib/serverUtils'; -import { getSession } from '@auth0/nextjs-auth0'; // --- Query Keys --- @@ -30,11 +29,10 @@ const staleTime = 1000 * 60; // 1 minute // --- Fetchers --- export function useWorkspace(workspaceId: string) { - const queryClient = useQueryClient(); return useQuery({ - queryKey: workspaceObjectKey(workspaceId), - queryFn: () => fetchWorkspaceData(workspaceId, queryClient), - select: (data) => data ?? [], // Returns an empty array if data isn't available yet + queryKey: ['workspace', workspaceId], + queryFn: () => fetchWorkspaceDataAction(workspaceId), + select: (data) => data ?? [], enabled: !!workspaceId, staleTime, }); @@ -126,20 +124,7 @@ export function useSessionsStats(sessionIds: string[]) { export function useAvailableSessions() { return useQuery({ queryKey: availableSessionsKey(), - queryFn: async () => { - // Move getAllAvailableSessionIds logic here - const session = await getSession(); - const userId = session?.user?.sub; - const availableResources = await db.getResourcesForUser(userId, "SESSION", ["resource_id"]); - const availableSessionsIds = availableResources.map((r) => r.resource_id).filter((id) => id !== 'global'); - - if (availableSessionsIds.length > 0) { - return await db.getHostSessionsForIds(availableSessionsIds, [ - 'id', 'topic', 'start_time' - ]); - } - return []; - }, + queryFn: () => fetchAvailableSessionsAction(), staleTime, }); } @@ -147,20 +132,7 @@ export function useAvailableSessions() { export function useAvailableWorkspaces() { return useQuery({ queryKey: ['available-workspaces-for-user'], - queryFn: async () => { - const session = await getSession(); - const userId = session?.user?.sub; - - if (!userId) { - return []; - } - - const resources = await db.getResourcesForUser(userId, 'WORKSPACE'); - if (!resources.length) return []; - - const workspaceIds = resources.map(resource => resource.resource_id); - return await db.getWorkspacesForIds(workspaceIds, ['id', 'title']); - }, + queryFn: () => fetchAvailableWorkspacesAction(), staleTime, }); } @@ -290,7 +262,7 @@ export function useSummary(resourceId: string, initialSummary?: string, isProjec enabled: !!resourceId, initialData: initialSummary, staleTime, - refetchOnWindowFocus: false, + refetchOnWindowFocus: true, }); } From bfc7e162f226086cf12671033acc2b6b43cc05b4 Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 4 Aug 2025 14:02:48 +1200 Subject: [PATCH 26/27] Empty transcript message --- src/components/SessionResult/TranscriptPopup.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/SessionResult/TranscriptPopup.tsx b/src/components/SessionResult/TranscriptPopup.tsx index fcd7727e..f55f0903 100644 --- a/src/components/SessionResult/TranscriptPopup.tsx +++ b/src/components/SessionResult/TranscriptPopup.tsx @@ -1,12 +1,11 @@ import { SessionRating } from '@/lib/schema'; -import { X, MessageSquare, Loader2 } from 'lucide-react'; +import { X, MessageSquare } from 'lucide-react'; import { useEffect, useState } from 'react'; import { ChatMessage } from '../ChatMessage'; import { Spinner } from '../icons'; import { Button } from '../ui/button'; import { useMessages } from '@/stores/SessionStore'; import { getThreadRating } from '@/lib/db'; -import { ParticipantsTableData } from './SessionParticipantsTable'; const EMOJI_RATINGS = [ { @@ -46,7 +45,6 @@ export default function TranscriptPopup({ userName: string; }) { const [rating, setRating] = useState(null); - console.log('Getting messages for threadId ', threadId) const { data: messages = [], isLoading } = useMessages(threadId); useEffect(() => { @@ -116,7 +114,7 @@ export default function TranscriptPopup({

Loading conversation...

- ) : ( + ) : messages.length > 0 ? (
{messages.map((message, index) => ( ))}
+ ) : ( +
+
+

Oh, nobody said anything yet ... ¯\_(ツ)_/¯

+
+
)}
From 4aa943bb9d5ae9aa65c90043e2ddfd03375eef15 Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 4 Aug 2025 14:02:48 +1200 Subject: [PATCH 27/27] Manual Summary Status update indicator --- src/hooks/useSummaryUpdateManager.ts | 8 +------- src/lib/summary-update-manager.ts | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/hooks/useSummaryUpdateManager.ts b/src/hooks/useSummaryUpdateManager.ts index 16d67eda..fe5e1131 100644 --- a/src/hooks/useSummaryUpdateManager.ts +++ b/src/hooks/useSummaryUpdateManager.ts @@ -91,7 +91,7 @@ export function useSummaryUpdateManager( // Pass the current summary status data and the mutation callback to the singleton. // The singleton will handle the debouncing and actual update scheduling. - summaryUpdateManager.scheduleUpdate(resourceId, summaryStatus, startUpdateMutation); + summaryUpdateManager.checkShouldUpdate(resourceId, summaryStatus, startUpdateMutation); }, [ summaryStatus, // Trigger when lastEdit or lastSummaryUpdate changes isSummaryStatusLoading, @@ -101,26 +101,20 @@ export function useSummaryUpdateManager( /** * Function to manually trigger a summary update immediately, bypassing the debounce. - * This is typically called by a UI action (e.g., a "Refresh" button). */ const startUpdateNow = useCallback(async () => { try { // Delegate the immediate update logic to the singleton. await summaryUpdateManager.startUpdateNow(resourceId, startUpdateMutation); } catch (err) { - // Error is already logged by the singleton, re-throw if the component needs to catch it. throw err; } }, [resourceId, startUpdateMutation]); return { - // The current refresh status, synchronized from the singleton. status: currentStatus, - // Loading state for the initial summary status query. isLoading: isSummaryStatusLoading, - // Combined error from summary status query or the update mutation. error: summaryStatusError || summaryUpdater.error, - // Function to manually trigger an update. startUpdateNow, }; } \ No newline at end of file diff --git a/src/lib/summary-update-manager.ts b/src/lib/summary-update-manager.ts index a294ed19..b7e7b3ed 100644 --- a/src/lib/summary-update-manager.ts +++ b/src/lib/summary-update-manager.ts @@ -64,6 +64,7 @@ class SummaryUpdateManagerClass { if (this.statuses.get(resourceId) === status) { return; } + console.log(`Status updated to ${status}`) this.statuses.set(resourceId, status); this.notifySubscribers(resourceId, status); } @@ -137,30 +138,30 @@ class SummaryUpdateManagerClass { } /** - * Schedules a summary update for a resource with a 30-second debounce. + * Checks whether to schedules a summary update for a resource with a 30-second debounce. * This method should be called whenever a potential change is detected (e.g., lastEdit timestamp changes). - * It will only trigger an actual update after 30 seconds of inactivity for that resource. + * It will only trigger an actual update after 30 seconds of inactivity for that resource, and replaces previous schedules. * @param resourceId The ID of the resource (e.g., session or workspace). * @param summaryStatus The current summary status data (lastEdit, lastSummaryUpdate). * @param startUpdateCallback A function that, when called, initiates the actual summary update (e.g., a TanStack Query mutation). */ - public scheduleUpdate( + public checkShouldUpdate( resourceId: string, summaryStatus: SummaryStatusData, startUpdateCallback: () => Promise ): void { - // 1. Guard: If summary is already up to date, clear any pending updates and set status. + // 1. Guard: If an update is currently in progress, let it run and do not schedule another. + if (this.getStatus(resourceId) === RefreshStatus.UpdateStarted) { + return; + } + + // 2. Guard: If summary is already up to date, clear any pending updates and set status. if (summaryStatus.lastSummaryUpdate >= summaryStatus.lastEdit) { this.setStatus(resourceId, RefreshStatus.UpToDate); this.clearScheduledUpdate(resourceId); // Ensure no pending updates return; } - // 2. Guard: If an update is currently in progress, do not schedule another. - if (this.getStatus(resourceId) === RefreshStatus.UpdateStarted) { - return; - } - // 3. Guard: Check if we've already registered this specific edit. // This prevents re-scheduling if the lastEdit hasn't genuinely advanced since last check. const lastRegistered = this.lastRegisteredEdits.get(resourceId) ?? 0; @@ -194,19 +195,18 @@ class SummaryUpdateManagerClass { * @param startUpdateCallback A function that initiates the actual summary update. * @returns A Promise that resolves when the update is complete, or rejects if it fails. */ - public async startUpdateNow(resourceId: string, startUpdateCallback: () => Promise): Promise { - // Clear any pending debounced update for this resource - this.clearScheduledUpdate(resourceId); - + public async startUpdateNow(resourceId: string, startUpdateCallback: () => Promise, manuallyTriggered: boolean = false): Promise { // Guard: Prevent starting if an update is already running if (this.getStatus(resourceId) === RefreshStatus.UpdateStarted) { console.warn(`[SummaryUpdateManager] Manual update for ${resourceId} aborted: already running.`); return; } + // Clear any other pending updates for this resource + this.clearScheduledUpdate(resourceId); // Set status to 'UpdateStarted' this.setStatus(resourceId, RefreshStatus.UpdateStarted); - + try { await startUpdateCallback(); // Execute the actual update this.setStatus(resourceId, RefreshStatus.UpToDate);